/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:expandtab:shiftwidth=4:tabstop=4: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsWindow.h"#include"mozilla/ArrayUtils.h"#include"mozilla/EventForwards.h"#include"mozilla/MiscEvents.h"#include"mozilla/MouseEvents.h"#include"mozilla/RefPtr.h"#include"mozilla/TextEventDispatcher.h"#include"mozilla/TextEvents.h"#include"mozilla/TimeStamp.h"#include"mozilla/TouchEvents.h"#include"mozilla/UniquePtrExtensions.h"#include<algorithm>#include"GeckoProfiler.h"#include"prlink.h"#include"nsGTKToolkit.h"#include"nsIRollupListener.h"#include"nsIDOMNode.h"#include"nsWidgetsCID.h"#include"nsDragService.h"#include"nsIWidgetListener.h"#include"nsIScreenManager.h"#include"SystemTimeConverter.h"#include"nsGtkKeyUtils.h"#include"nsGtkCursors.h"#include"ScreenHelperGTK.h"#include<gtk/gtk.h>#if (MOZ_WIDGET_GTK == 3)#include<gtk/gtkx.h>#endif#ifdef MOZ_X11#include<gdk/gdkx.h>#include<X11/Xatom.h>#include<X11/extensions/XShm.h>#include<X11/extensions/shape.h>#if (MOZ_WIDGET_GTK == 3)#include<gdk/gdkkeysyms-compat.h>#endif#if (MOZ_WIDGET_GTK == 2)#include"gtk2xtbin.h"#endif#endif /* MOZ_X11 */#include<gdk/gdkkeysyms.h>#if (MOZ_WIDGET_GTK == 2)#include<gtk/gtkprivate.h>#endif#include"nsGkAtoms.h"#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION#define SN_API_NOT_YET_FROZEN#include<startup-notification-1.0/libsn/sn.h>#endif#include"mozilla/Assertions.h"#include"mozilla/Likely.h"#include"mozilla/Preferences.h"#include"nsIPrefService.h"#include"nsIGConfService.h"#include"nsIServiceManager.h"#include"nsIStringBundle.h"#include"nsGfxCIID.h"#include"nsGtkUtils.h"#include"nsIObserverService.h"#include"mozilla/layers/LayersTypes.h"#include"nsIIdleServiceInternal.h"#include"nsIPropertyBag2.h"#include"GLContext.h"#include"gfx2DGlue.h"#ifdef ACCESSIBILITY#include"mozilla/a11y/Accessible.h"#include"mozilla/a11y/Platform.h"#include"nsAccessibilityService.h"usingnamespacemozilla;usingnamespacemozilla::widget;#endif/* For SetIcon */#include"nsAppDirectoryServiceDefs.h"#include"nsXPIDLString.h"#include"nsIFile.h"/* SetCursor(imgIContainer*) */#include<gdk/gdk.h>#include<wchar.h>#include"imgIContainer.h"#include"nsGfxCIID.h"#include"nsImageToPixbuf.h"#include"nsIInterfaceRequestorUtils.h"#include"ClientLayerManager.h"#include"gfxPlatformGtk.h"#include"gfxContext.h"#include"gfxImageSurface.h"#include"gfxUtils.h"#include"Layers.h"#include"GLContextProvider.h"#include"mozilla/gfx/2D.h"#include"mozilla/gfx/HelpersCairo.h"#include"mozilla/layers/CompositorBridgeParent.h"#include"mozilla/layers/CompositorThread.h"#ifdef MOZ_X11#include"X11CompositorWidget.h"#include"gfxXlibSurface.h"#include"WindowSurfaceX11Image.h"#include"WindowSurfaceX11SHM.h"#include"WindowSurfaceXRender.h"#endif // MOZ_X11#include"nsShmImage.h"#include"nsIDOMWheelEvent.h"#include"NativeKeyBindings.h"#include<dlfcn.h>#include"mozilla/layers/APZCTreeManager.h"usingnamespacemozilla;usingnamespacemozilla::gfx;usingnamespacemozilla::widget;usingnamespacemozilla::layers;usingmozilla::gl::GLContext;// Don't put more than this many rects in the dirty region, just fluff// out to the bounding-box if there are more#define MAX_RECTS_IN_REGION 100constgintkEvents=GDK_EXPOSURE_MASK|GDK_STRUCTURE_MASK|GDK_VISIBILITY_NOTIFY_MASK|GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|#if GTK_CHECK_VERSION(3,4,0)GDK_SMOOTH_SCROLL_MASK|GDK_TOUCH_MASK|#endifGDK_SCROLL_MASK|GDK_POINTER_MOTION_MASK|GDK_PROPERTY_CHANGE_MASK;/* utility functions */staticboolis_mouse_in_window(GdkWindow*aWindow,gdoubleaMouseX,gdoubleaMouseY);staticnsWindow*get_window_for_gtk_widget(GtkWidget*widget);staticnsWindow*get_window_for_gdk_window(GdkWindow*window);staticGtkWidget*get_gtk_widget_for_gdk_window(GdkWindow*window);staticGdkCursor*get_gtk_cursor(nsCursoraCursor);staticGdkWindow*get_inner_gdk_window(GdkWindow*aWindow,gintx,ginty,gint*retx,gint*rety);staticintis_parent_ungrab_enter(GdkEventCrossing*aEvent);staticintis_parent_grab_leave(GdkEventCrossing*aEvent);staticvoidGetBrandName(nsXPIDLString&brandName);/* callbacks from widgets */#if (MOZ_WIDGET_GTK == 2)staticgbooleanexpose_event_cb(GtkWidget*widget,GdkEventExpose*event);#elsestaticgbooleanexpose_event_cb(GtkWidget*widget,cairo_t*rect);#endifstaticgbooleanconfigure_event_cb(GtkWidget*widget,GdkEventConfigure*event);staticvoidcontainer_unrealize_cb(GtkWidget*widget);staticvoidsize_allocate_cb(GtkWidget*widget,GtkAllocation*allocation);staticgbooleandelete_event_cb(GtkWidget*widget,GdkEventAny*event);staticgbooleanenter_notify_event_cb(GtkWidget*widget,GdkEventCrossing*event);staticgbooleanleave_notify_event_cb(GtkWidget*widget,GdkEventCrossing*event);staticgbooleanmotion_notify_event_cb(GtkWidget*widget,GdkEventMotion*event);staticgbooleanbutton_press_event_cb(GtkWidget*widget,GdkEventButton*event);staticgbooleanbutton_release_event_cb(GtkWidget*widget,GdkEventButton*event);staticgbooleanfocus_in_event_cb(GtkWidget*widget,GdkEventFocus*event);staticgbooleanfocus_out_event_cb(GtkWidget*widget,GdkEventFocus*event);staticgbooleankey_press_event_cb(GtkWidget*widget,GdkEventKey*event);staticgbooleankey_release_event_cb(GtkWidget*widget,GdkEventKey*event);staticgbooleanproperty_notify_event_cb(GtkWidget*widget,GdkEventProperty*event);staticgbooleanscroll_event_cb(GtkWidget*widget,GdkEventScroll*event);staticgbooleanvisibility_notify_event_cb(GtkWidget*widget,GdkEventVisibility*event);staticvoidhierarchy_changed_cb(GtkWidget*widget,GtkWidget*previous_toplevel);staticgbooleanwindow_state_event_cb(GtkWidget*widget,GdkEventWindowState*event);staticvoidtheme_changed_cb(GtkSettings*settings,GParamSpec*pspec,nsWindow*data);staticvoidcheck_resize_cb(GtkContainer*container,gpointeruser_data);staticvoidcomposited_changed_cb(GtkWidget*widget,gpointeruser_data);#if (MOZ_WIDGET_GTK == 3)staticvoidscale_changed_cb(GtkWidget*widget,GParamSpec*aPSpec,gpointeraPointer);#endif#if GTK_CHECK_VERSION(3,4,0)staticgbooleantouch_event_cb(GtkWidget*aWidget,GdkEventTouch*aEvent);#endifstaticnsWindow*GetFirstNSWindowForGDKWindow(GdkWindow*aGdkWindow);#ifdef __cplusplusextern"C"{#endif /* __cplusplus */#ifdef MOZ_X11staticGdkFilterReturnpopup_take_focus_filter(GdkXEvent*gdk_xevent,GdkEvent*event,gpointerdata);#endif /* MOZ_X11 */#ifdef __cplusplus}#endif /* __cplusplus */staticgbooleandrag_motion_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,guintaTime,gpointeraData);staticvoiddrag_leave_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,guintaTime,gpointeraData);staticgbooleandrag_drop_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,guintaTime,gpointeraData);staticvoiddrag_data_received_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,GtkSelectionData*aSelectionData,guintaInfo,guint32aTime,gpointeraData);/* initialization static functions */staticnsresultinitialize_prefs(void);staticguint32sLastUserInputTime=GDK_CURRENT_TIME;staticguint32sRetryGrabTime;staticSystemTimeConverter<guint32>&TimeConverter(){staticSystemTimeConverter<guint32>sTimeConverterSingleton;returnsTimeConverterSingleton;}namespacemozilla{classCurrentX11TimeGetter{public:explicitCurrentX11TimeGetter(GdkWindow*aWindow):mWindow(aWindow),mAsyncUpdateStart(){}guint32GetCurrentTime()const{returngdk_x11_get_server_time(mWindow);}voidGetTimeAsyncForPossibleBackwardsSkew(constTimeStamp&aNow){// Check for in-flight requestif(!mAsyncUpdateStart.IsNull()){return;}mAsyncUpdateStart=aNow;Display*xDisplay=GDK_WINDOW_XDISPLAY(mWindow);WindowxWindow=GDK_WINDOW_XID(mWindow);unsignedcharc='a';AtomtimeStampPropAtom=TimeStampPropAtom();XChangeProperty(xDisplay,xWindow,timeStampPropAtom,timeStampPropAtom,8,PropModeReplace,&c,1);XFlush(xDisplay);}gbooleanPropertyNotifyHandler(GtkWidget*aWidget,GdkEventProperty*aEvent){if(aEvent->atom!=gdk_x11_xatom_to_atom(TimeStampPropAtom())){returnFALSE;}guint32eventTime=aEvent->time;TimeStamplowerBound=mAsyncUpdateStart;TimeConverter().CompensateForBackwardsSkew(eventTime,lowerBound);mAsyncUpdateStart=TimeStamp();returnTRUE;}private:staticAtomTimeStampPropAtom(){returngdk_x11_get_xatom_by_name_for_display(gdk_display_get_default(),"GDK_TIMESTAMP_PROP");}// This is safe because this class is stored as a member of mWindow and// won't outlive it.GdkWindow*mWindow;TimeStampmAsyncUpdateStart;};}// namespace mozillastaticNS_DEFINE_IID(kCDragServiceCID,NS_DRAGSERVICE_CID);// The window from which the focus manager asks us to dispatch key events.staticnsWindow*gFocusWindow=nullptr;staticboolgBlockActivateEvent=false;staticboolgGlobalsInitialized=false;staticboolgRaiseWindows=true;#if GTK_CHECK_VERSION(3,4,0)staticuint32_tgLastTouchID=0;#endif#define NS_WINDOW_TITLE_MAX_LENGTH 4095// If after selecting profile window, the startup fail, please refer to// http://bugzilla.gnome.org/show_bug.cgi?id=88940// needed for imgIContainer cursors// GdkDisplay* was added in 2.2typedefstruct_GdkDisplayGdkDisplay;#define kWindowPositionSlop 20// cursor cachestaticGdkCursor*gCursorCache[eCursorCount];staticGtkWidget*gInvisibleContainer=nullptr;// Sometimes this actually also includes the state of the modifier keys, but// only the button state bits are used.staticguintgButtonState;staticinlineint32_tGetBitmapStride(int32_twidth){#if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2)return(width+7)/8;#elsereturncairo_format_stride_for_width(CAIRO_FORMAT_A1,width);#endif}staticinlineboolTimestampIsNewerThan(guint32a,guint32b){// Timestamps are just the least significant bits of a monotonically// increasing function, and so the use of unsigned overflow arithmetic.returna-b<=G_MAXUINT32/2;}staticvoidUpdateLastInputEventTime(void*aGdkEvent){nsCOMPtr<nsIIdleServiceInternal>idleService=do_GetService("@mozilla.org/widget/idleservice;1");if(idleService){idleService->ResetIdleTimeOut(0);}guinttimestamp=gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));if(timestamp==GDK_CURRENT_TIME)return;sLastUserInputTime=timestamp;}NS_IMPL_ISUPPORTS_INHERITED0(nsWindow,nsBaseWidget)nsWindow::nsWindow(){mIsTopLevel=false;mIsDestroyed=false;mListenForResizes=false;mNeedsDispatchResized=false;mIsShown=false;mNeedsShow=false;mEnabled=true;mCreated=false;#if GTK_CHECK_VERSION(3,4,0)mHandleTouchEvent=false;#endifmIsDragPopup=false;mIsX11Display=GDK_IS_X11_DISPLAY(gdk_display_get_default());mContainer=nullptr;mGdkWindow=nullptr;mShell=nullptr;mHasMappedToplevel=false;mIsFullyObscured=false;mRetryPointerGrab=false;mWindowType=eWindowType_child;mSizeState=nsSizeMode_Normal;mLastSizeMode=nsSizeMode_Normal;mSizeConstraints.mMaxSize=GetSafeWindowSize(mSizeConstraints.mMaxSize);#ifdef MOZ_X11mOldFocusWindow=0;mXDisplay=nullptr;mXWindow=X11None;mXVisual=nullptr;mXDepth=0;#endif /* MOZ_X11 */if(!gGlobalsInitialized){gGlobalsInitialized=true;// It's OK if either of these fail, but it may not be one day.initialize_prefs();}mLastMotionPressure=0;#ifdef ACCESSIBILITYmRootAccessible=nullptr;#endifmIsTransparent=false;mTransparencyBitmap=nullptr;mTransparencyBitmapWidth=0;mTransparencyBitmapHeight=0;#if GTK_CHECK_VERSION(3,4,0)mLastScrollEventTime=GDK_CURRENT_TIME;#endifmPendingConfigures=0;}nsWindow::~nsWindow(){LOG(("nsWindow::~nsWindow() [%p]\n",(void*)this));delete[]mTransparencyBitmap;mTransparencyBitmap=nullptr;Destroy();}/* static */voidnsWindow::ReleaseGlobals(){for(auto&cursor:gCursorCache){if(cursor){#if (MOZ_WIDGET_GTK == 3)g_object_unref(cursor);#elsegdk_cursor_unref(cursor);#endifcursor=nullptr;}}}voidnsWindow::CommonCreate(nsIWidget*aParent,boolaListenForResizes){mParent=aParent;mListenForResizes=aListenForResizes;mCreated=true;}voidnsWindow::DispatchActivateEvent(void){NS_ASSERTION(mContainer||mIsDestroyed,"DispatchActivateEvent only intended for container windows");#ifdef ACCESSIBILITYDispatchActivateEventAccessible();#endif //ACCESSIBILITYif(mWidgetListener)mWidgetListener->WindowActivated();}voidnsWindow::DispatchDeactivateEvent(void){if(mWidgetListener)mWidgetListener->WindowDeactivated();#ifdef ACCESSIBILITYDispatchDeactivateEventAccessible();#endif //ACCESSIBILITY}voidnsWindow::DispatchResized(){mNeedsDispatchResized=false;if(mWidgetListener){mWidgetListener->WindowResized(this,mBounds.width,mBounds.height);}if(mAttachedWidgetListener){mAttachedWidgetListener->WindowResized(this,mBounds.width,mBounds.height);}}voidnsWindow::MaybeDispatchResized(){if(mNeedsDispatchResized&&!mIsDestroyed){DispatchResized();}}nsIWidgetListener*nsWindow::GetListener(){returnmAttachedWidgetListener?mAttachedWidgetListener:mWidgetListener;}nsresultnsWindow::DispatchEvent(WidgetGUIEvent*aEvent,nsEventStatus&aStatus){#ifdef DEBUGdebug_DumpEvent(stdout,aEvent->mWidget,aEvent,"something",0);#endifaStatus=nsEventStatus_eIgnore;nsIWidgetListener*listener=GetListener();if(listener){aStatus=listener->HandleEvent(aEvent,mUseAttachedEvents);}returnNS_OK;}voidnsWindow::OnDestroy(void){if(mOnDestroyCalled)return;mOnDestroyCalled=true;// Prevent deletion.nsCOMPtr<nsIWidget>kungFuDeathGrip=this;// release references to children, device context, toolkit + app shellnsBaseWidget::OnDestroy();// Remove association between this object and its parent and siblings.nsBaseWidget::Destroy();mParent=nullptr;NotifyWindowDestroyed();}boolnsWindow::AreBoundsSane(void){if(mBounds.width>0&&mBounds.height>0)returntrue;returnfalse;}staticGtkWidget*EnsureInvisibleContainer(){if(!gInvisibleContainer){// GtkWidgets need to be anchored to a GtkWindow to be realized (to// have a window). Using GTK_WINDOW_POPUP rather than// GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less// initialization and window manager interaction.GtkWidget*window=gtk_window_new(GTK_WINDOW_POPUP);gInvisibleContainer=moz_container_new();gtk_container_add(GTK_CONTAINER(window),gInvisibleContainer);gtk_widget_realize(gInvisibleContainer);}returngInvisibleContainer;}staticvoidCheckDestroyInvisibleContainer(){NS_PRECONDITION(gInvisibleContainer,"oh, no");if(!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))){// No children, so not in use.// Make sure to destroy the GtkWindow also.gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));gInvisibleContainer=nullptr;}}// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of// the GdkWindow hierarchy to aNewWidget.staticvoidSetWidgetForHierarchy(GdkWindow*aWindow,GtkWidget*aOldWidget,GtkWidget*aNewWidget){gpointerdata;gdk_window_get_user_data(aWindow,&data);if(data!=aOldWidget){if(!GTK_IS_WIDGET(data))return;auto*widget=static_cast<GtkWidget*>(data);if(gtk_widget_get_parent(widget)!=aOldWidget)return;// This window belongs to a child widget, which will no longer be a// child of aOldWidget.gtk_widget_reparent(widget,aNewWidget);return;}GList*children=gdk_window_get_children(aWindow);for(GList*list=children;list;list=list->next){SetWidgetForHierarchy(GDK_WINDOW(list->data),aOldWidget,aNewWidget);}g_list_free(children);gdk_window_set_user_data(aWindow,aNewWidget);}// Walk the list of child windows and call destroy on them.voidnsWindow::DestroyChildWindows(){if(!mGdkWindow)return;while(GList*children=gdk_window_peek_children(mGdkWindow)){GdkWindow*child=GDK_WINDOW(children->data);nsWindow*kid=get_window_for_gdk_window(child);if(kid){kid->Destroy();}else{// This child is not an nsWindow.// Destroy the child GtkWidget.gpointerdata;gdk_window_get_user_data(child,&data);if(GTK_IS_WIDGET(data)){gtk_widget_destroy(static_cast<GtkWidget*>(data));}}}}voidnsWindow::Destroy(){if(mIsDestroyed||!mCreated)return;LOG(("nsWindow::Destroy [%p]\n",(void*)this));mIsDestroyed=true;mCreated=false;/** Need to clean our LayerManager up while still alive */if(mLayerManager){mLayerManager->Destroy();}mLayerManager=nullptr;// It is safe to call DestroyeCompositor several times (here and// in the parent class) since it will take effect only once.// The reason we call it here is because on gtk platforms we need// to destroy the compositor before we destroy the gdk window (which// destroys the the gl context attached to it).DestroyCompositor();#ifdef MOZ_X11// Ensure any resources assigned to the window get cleaned up first// to avoid double-freeing.mSurfaceProvider.CleanupResources();#endifClearCachedResources();g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),FuncToGpointer(theme_changed_cb),this);nsIRollupListener*rollupListener=nsBaseWidget::GetActiveRollupListener();if(rollupListener){nsCOMPtr<nsIWidget>rollupWidget=rollupListener->GetRollupWidget();if(static_cast<nsIWidget*>(this)==rollupWidget){rollupListener->Rollup(0,false,nullptr,nullptr);}}// dragService will be null after shutdown of the service manager.RefPtr<nsDragService>dragService=nsDragService::GetInstance();if(dragService&&this==dragService->GetMostRecentDestWindow()){dragService->ScheduleLeaveEvent();}NativeShow(false);if(mIMContext){mIMContext->OnDestroyWindow(this);}// make sure that we remove ourself as the focus windowif(gFocusWindow==this){LOGFOCUS(("automatically losing focus...\n"));gFocusWindow=nullptr;}GtkWidget*owningWidget=GetMozContainerWidget();if(mShell){gtk_widget_destroy(mShell);mShell=nullptr;mContainer=nullptr;MOZ_ASSERT(!mGdkWindow,"mGdkWindow should be NULL when mContainer is destroyed");}elseif(mContainer){gtk_widget_destroy(GTK_WIDGET(mContainer));mContainer=nullptr;MOZ_ASSERT(!mGdkWindow,"mGdkWindow should be NULL when mContainer is destroyed");}elseif(mGdkWindow){// Destroy child windows to ensure that their mThebesSurfaces are// released and to remove references from GdkWindows back to their// container widget. (OnContainerUnrealize() does this when the// MozContainer widget is destroyed.)DestroyChildWindows();gdk_window_set_user_data(mGdkWindow,nullptr);g_object_set_data(G_OBJECT(mGdkWindow),"nsWindow",nullptr);gdk_window_destroy(mGdkWindow);mGdkWindow=nullptr;}if(gInvisibleContainer&&owningWidget==gInvisibleContainer){CheckDestroyInvisibleContainer();}#ifdef ACCESSIBILITYif(mRootAccessible){mRootAccessible=nullptr;}#endif// Save until last because OnDestroy() may cause us to be deleted.OnDestroy();}nsIWidget*nsWindow::GetParent(void){returnmParent;}floatnsWindow::GetDPI(){GdkScreen*screen=gdk_display_get_default_screen(gdk_display_get_default());doubleheightInches=gdk_screen_get_height_mm(screen)/MM_PER_INCH_FLOAT;if(heightInches<0.25){// Something's broken, but we'd better not crash.return96.0f;}returnfloat(gdk_screen_get_height(screen)/heightInches);}doublensWindow::GetDefaultScaleInternal(){returnGdkScaleFactor()*gfxPlatformGtk::GetDPIScale();}voidnsWindow::SetParent(nsIWidget*aNewParent){if(mContainer||!mGdkWindow){NS_NOTREACHED("nsWindow::SetParent called illegally");return;}nsCOMPtr<nsIWidget>kungFuDeathGrip=this;if(mParent){mParent->RemoveChild(this);}mParent=aNewParent;GtkWidget*oldContainer=GetMozContainerWidget();if(!oldContainer){// The GdkWindows have been destroyed so there is nothing else to// reparent.MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),"live GdkWindow with no widget");return;}if(aNewParent){aNewParent->AddChild(this);ReparentNativeWidget(aNewParent);}else{// aNewParent is nullptr, but reparent to a hidden window to avoid// destroying the GdkWindow and its descendants.// An invisible container widget is needed to hold descendant// GtkWidgets.GtkWidget*newContainer=EnsureInvisibleContainer();GdkWindow*newParentWindow=gtk_widget_get_window(newContainer);ReparentNativeWidgetInternal(aNewParent,newContainer,newParentWindow,oldContainer);}}boolnsWindow::WidgetTypeSupportsAcceleration(){return!IsSmallPopup();}voidnsWindow::ReparentNativeWidget(nsIWidget*aNewParent){NS_PRECONDITION(aNewParent,"");NS_ASSERTION(!mIsDestroyed,"");NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed,"");GtkWidget*oldContainer=GetMozContainerWidget();if(!oldContainer){// The GdkWindows have been destroyed so there is nothing else to// reparent.MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),"live GdkWindow with no widget");return;}MOZ_ASSERT(!gdk_window_is_destroyed(mGdkWindow),"destroyed GdkWindow with widget");auto*newParent=static_cast<nsWindow*>(aNewParent);GdkWindow*newParentWindow=newParent->mGdkWindow;GtkWidget*newContainer=newParent->GetMozContainerWidget();GtkWindow*shell=GTK_WINDOW(mShell);if(shell&>k_window_get_transient_for(shell)){GtkWindow*topLevelParent=GTK_WINDOW(gtk_widget_get_toplevel(newContainer));gtk_window_set_transient_for(shell,topLevelParent);}ReparentNativeWidgetInternal(aNewParent,newContainer,newParentWindow,oldContainer);}voidnsWindow::ReparentNativeWidgetInternal(nsIWidget*aNewParent,GtkWidget*aNewContainer,GdkWindow*aNewParentWindow,GtkWidget*aOldContainer){if(!aNewContainer){// The new parent GdkWindow has been destroyed.MOZ_ASSERT(!aNewParentWindow||gdk_window_is_destroyed(aNewParentWindow),"live GdkWindow with no widget");Destroy();}else{if(aNewContainer!=aOldContainer){MOZ_ASSERT(!gdk_window_is_destroyed(aNewParentWindow),"destroyed GdkWindow with widget");SetWidgetForHierarchy(mGdkWindow,aOldContainer,aNewContainer);if(aOldContainer==gInvisibleContainer){CheckDestroyInvisibleContainer();}}if(!mIsTopLevel){gdk_window_reparent(mGdkWindow,aNewParentWindow,DevicePixelsToGdkCoordRoundDown(mBounds.x),DevicePixelsToGdkCoordRoundDown(mBounds.y));}}auto*newParent=static_cast<nsWindow*>(aNewParent);boolparentHasMappedToplevel=newParent&&newParent->mHasMappedToplevel;if(mHasMappedToplevel!=parentHasMappedToplevel){SetHasMappedToplevel(parentHasMappedToplevel);}}voidnsWindow::SetModal(boolaModal){LOG(("nsWindow::SetModal [%p] %d\n",(void*)this,aModal));if(mIsDestroyed)return;if(!mIsTopLevel||!mShell)return;gtk_window_set_modal(GTK_WINDOW(mShell),aModal?TRUE:FALSE);}// nsIWidget method, which means IsShown.boolnsWindow::IsVisible()const{returnmIsShown;}voidnsWindow::RegisterTouchWindow(){#if GTK_CHECK_VERSION(3,4,0)mHandleTouchEvent=true;mTouches.Clear();#endif}voidnsWindow::ConstrainPosition(boolaAllowSlop,int32_t*aX,int32_t*aY){if(!mIsTopLevel||!mShell)return;doubledpiScale=GetDefaultScale().scale;// we need to use the window size in logical screen pixelsint32_tlogWidth=std::max(NSToIntRound(mBounds.width/dpiScale),1);int32_tlogHeight=std::max(NSToIntRound(mBounds.height/dpiScale),1);/* get our playing field. use the current screen, or failing that for any reason, use device caps for the default screen. */nsCOMPtr<nsIScreen>screen;nsCOMPtr<nsIScreenManager>screenmgr=do_GetService("@mozilla.org/gfx/screenmanager;1");if(screenmgr){screenmgr->ScreenForRect(*aX,*aY,logWidth,logHeight,getter_AddRefs(screen));}// We don't have any screen so leave the coordinates as isif(!screen)return;nsIntRectscreenRect;if(mSizeMode!=nsSizeMode_Fullscreen){// For normalized windows, use the desktop work area.screen->GetAvailRectDisplayPix(&screenRect.x,&screenRect.y,&screenRect.width,&screenRect.height);}else{// For full screen windows, use the desktop.screen->GetRectDisplayPix(&screenRect.x,&screenRect.y,&screenRect.width,&screenRect.height);}if(aAllowSlop){if(*aX<screenRect.x-logWidth+kWindowPositionSlop)*aX=screenRect.x-logWidth+kWindowPositionSlop;elseif(*aX>=screenRect.XMost()-kWindowPositionSlop)*aX=screenRect.XMost()-kWindowPositionSlop;if(*aY<screenRect.y-logHeight+kWindowPositionSlop)*aY=screenRect.y-logHeight+kWindowPositionSlop;elseif(*aY>=screenRect.YMost()-kWindowPositionSlop)*aY=screenRect.YMost()-kWindowPositionSlop;}else{if(*aX<screenRect.x)*aX=screenRect.x;elseif(*aX>=screenRect.XMost()-logWidth)*aX=screenRect.XMost()-logWidth;if(*aY<screenRect.y)*aY=screenRect.y;elseif(*aY>=screenRect.YMost()-logHeight)*aY=screenRect.YMost()-logHeight;}}voidnsWindow::SetSizeConstraints(constSizeConstraints&aConstraints){mSizeConstraints.mMinSize=GetSafeWindowSize(aConstraints.mMinSize);mSizeConstraints.mMaxSize=GetSafeWindowSize(aConstraints.mMaxSize);if(mShell){GdkGeometrygeometry;geometry.min_width=DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.width);geometry.min_height=DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.height);geometry.max_width=DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.width);geometry.max_height=DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.height);uint32_thints=0;if(aConstraints.mMinSize!=LayoutDeviceIntSize(0,0)){hints|=GDK_HINT_MIN_SIZE;}if(aConstraints.mMaxSize!=LayoutDeviceIntSize(NS_MAXSIZE,NS_MAXSIZE)){hints|=GDK_HINT_MAX_SIZE;}gtk_window_set_geometry_hints(GTK_WINDOW(mShell),nullptr,&geometry,GdkWindowHints(hints));}}voidnsWindow::Show(boolaState){if(aState==mIsShown)return;// Clear our cached resources when the window is hidden.if(mIsShown&&!aState){ClearCachedResources();}mIsShown=aState;LOG(("nsWindow::Show [%p] state %d\n",(void*)this,aState));if(aState){// Now that this window is shown, mHasMappedToplevel needs to be// tracked on viewable descendants.SetHasMappedToplevel(mHasMappedToplevel);}// Ok, someone called show on a window that isn't sized to a sane// value. Mark this window as needing to have Show() called on it// and return.if((aState&&!AreBoundsSane())||!mCreated){LOG(("\tbounds are insane or window hasn't been created yet\n"));mNeedsShow=true;return;}// If someone is hiding this widget, clear any needing show flag.if(!aState)mNeedsShow=false;#ifdef ACCESSIBILITYif(aState&&a11y::ShouldA11yBeEnabled())CreateRootAccessible();#endifNativeShow(aState);}voidnsWindow::Resize(doubleaWidth,doubleaHeight,boolaRepaint){doublescale=BoundsUseDesktopPixels()?GetDesktopToDeviceScale().scale:1.0;int32_twidth=NSToIntRound(scale*aWidth);int32_theight=NSToIntRound(scale*aHeight);ConstrainSize(&width,&height);// For top-level windows, aWidth and aHeight should possibly be// interpreted as frame bounds, but NativeResize treats these as window// bounds (Bug 581866).mBounds.SizeTo(width,height);if(!mCreated)return;NativeResize();NotifyRollupGeometryChange();// send a resize notification if this is a toplevelif(mIsTopLevel||mListenForResizes){DispatchResized();}}voidnsWindow::Resize(doubleaX,doubleaY,doubleaWidth,doubleaHeight,boolaRepaint){doublescale=BoundsUseDesktopPixels()?GetDesktopToDeviceScale().scale:1.0;int32_twidth=NSToIntRound(scale*aWidth);int32_theight=NSToIntRound(scale*aHeight);ConstrainSize(&width,&height);int32_tx=NSToIntRound(scale*aX);int32_ty=NSToIntRound(scale*aY);mBounds.x=x;mBounds.y=y;mBounds.SizeTo(width,height);if(!mCreated)return;NativeMoveResize();NotifyRollupGeometryChange();if(mIsTopLevel||mListenForResizes){DispatchResized();}}voidnsWindow::Enable(boolaState){mEnabled=aState;}boolnsWindow::IsEnabled()const{returnmEnabled;}voidnsWindow::Move(doubleaX,doubleaY){LOG(("nsWindow::Move [%p] %f %f\n",(void*)this,aX,aY));doublescale=BoundsUseDesktopPixels()?GetDesktopToDeviceScale().scale:1.0;int32_tx=NSToIntRound(aX*scale);int32_ty=NSToIntRound(aY*scale);if(mWindowType==eWindowType_toplevel||mWindowType==eWindowType_dialog){SetSizeMode(nsSizeMode_Normal);}// Since a popup window's x/y coordinates are in relation to to// the parent, the parent might have moved so we always move a// popup window.if(x==mBounds.x&&y==mBounds.y&&mWindowType!=eWindowType_popup)return;// XXX Should we do some AreBoundsSane check here?mBounds.x=x;mBounds.y=y;if(!mCreated)return;NativeMove();NotifyRollupGeometryChange();}voidnsWindow::NativeMove(){GdkPointpoint=DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());if(mIsTopLevel){gtk_window_move(GTK_WINDOW(mShell),point.x,point.y);}elseif(mGdkWindow){gdk_window_move(mGdkWindow,point.x,point.y);}}voidnsWindow::SetZIndex(int32_taZIndex){nsIWidget*oldPrev=GetPrevSibling();nsBaseWidget::SetZIndex(aZIndex);if(GetPrevSibling()==oldPrev){return;}NS_ASSERTION(!mContainer,"Expected Mozilla child widget");// We skip the nsWindows that don't have mGdkWindows.// These are probably in the process of being destroyed.if(!GetNextSibling()){// We're to be on top.if(mGdkWindow)gdk_window_raise(mGdkWindow);}else{// All the siblings before us need to be below our widget.for(nsWindow*w=this;w;w=static_cast<nsWindow*>(w->GetPrevSibling())){if(w->mGdkWindow)gdk_window_lower(w->mGdkWindow);}}}voidnsWindow::SetSizeMode(nsSizeModeaMode){LOG(("nsWindow::SetSizeMode [%p] %d\n",(void*)this,aMode));// Save the requested state.nsBaseWidget::SetSizeMode(aMode);// return if there's no shell or our current state is the same as// the mode we were just set to.if(!mShell||mSizeState==mSizeMode){return;}switch(aMode){casensSizeMode_Maximized:gtk_window_maximize(GTK_WINDOW(mShell));break;casensSizeMode_Minimized:gtk_window_iconify(GTK_WINDOW(mShell));break;casensSizeMode_Fullscreen:MakeFullScreen(true);break;default:// nsSizeMode_Normal, really.if(mSizeState==nsSizeMode_Minimized)gtk_window_deiconify(GTK_WINDOW(mShell));elseif(mSizeState==nsSizeMode_Maximized)gtk_window_unmaximize(GTK_WINDOW(mShell));break;}mSizeState=mSizeMode;}typedefvoid(*SetUserTimeFunc)(GdkWindow*aWindow,guint32aTimestamp);// This will become obsolete when new GTK APIs are widely supported,// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375staticvoidSetUserTimeAndStartupIDForActivatedWindow(GtkWidget*aWindow){nsGTKToolkit*GTKToolkit=nsGTKToolkit::GetToolkit();if(!GTKToolkit)return;nsAutoCStringdesktopStartupID;GTKToolkit->GetDesktopStartupID(&desktopStartupID);if(desktopStartupID.IsEmpty()){// We don't have the data we need. Fall back to an// approximation ... using the timestamp of the remote command// being received as a guess for the timestamp of the user event// that triggered it.uint32_ttimestamp=GTKToolkit->GetFocusTimestamp();if(timestamp){gdk_window_focus(gtk_widget_get_window(aWindow),timestamp);GTKToolkit->SetFocusTimestamp(0);}return;}#if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)// TODO - Implement for non-X11 Gtk backends (Bug 726479)if(GDK_IS_X11_DISPLAY(gdk_display_get_default())){GdkWindow*gdkWindow=gtk_widget_get_window(aWindow);GdkScreen*screen=gdk_window_get_screen(gdkWindow);SnDisplay*snd=sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),nullptr,nullptr);if(!snd)return;SnLauncheeContext*ctx=sn_launchee_context_new(snd,gdk_screen_get_number(screen),desktopStartupID.get());if(!ctx){sn_display_unref(snd);return;}if(sn_launchee_context_get_id_has_timestamp(ctx)){gdk_x11_window_set_user_time(gdkWindow,sn_launchee_context_get_timestamp(ctx));}sn_launchee_context_setup_window(ctx,gdk_x11_window_get_xid(gdkWindow));sn_launchee_context_complete(ctx);sn_launchee_context_unref(ctx);sn_display_unref(snd);}#endif// If we used the startup ID, that already contains the focus timestamp;// we don't want to reuse the timestamp next time we raise the windowGTKToolkit->SetFocusTimestamp(0);GTKToolkit->SetDesktopStartupID(EmptyCString());}/* static */guint32nsWindow::GetLastUserInputTime(){// gdk_x11_display_get_user_time tracks button and key presses,// DESKTOP_STARTUP_ID used to start the app, drop events from external// drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor// button and key releases. Therefore use the most recent of// gdk_x11_display_get_user_time and the last time that we have seen.guint32timestamp=gdk_x11_display_get_user_time(gdk_display_get_default());if(sLastUserInputTime!=GDK_CURRENT_TIME&&TimestampIsNewerThan(sLastUserInputTime,timestamp)){returnsLastUserInputTime;}returntimestamp;}nsresultnsWindow::SetFocus(boolaRaise){// Make sure that our owning widget has focus. If it doesn't try to// grab it. Note that we don't set our focus flag in this case.LOGFOCUS((" SetFocus %d [%p]\n",aRaise,(void*)this));GtkWidget*owningWidget=GetMozContainerWidget();if(!owningWidget)returnNS_ERROR_FAILURE;// Raise the window if someone passed in true and the prefs are// set properly.GtkWidget*toplevelWidget=gtk_widget_get_toplevel(owningWidget);if(gRaiseWindows&&aRaise&&toplevelWidget&&!gtk_widget_has_focus(owningWidget)&&!gtk_widget_has_focus(toplevelWidget)){GtkWidget*top_window=GetToplevelWidget();if(top_window&&(gtk_widget_get_visible(top_window))){gdk_window_show_unraised(gtk_widget_get_window(top_window));// Unset the urgency hint if possible.SetUrgencyHint(top_window,false);}}RefPtr<nsWindow>owningWindow=get_window_for_gtk_widget(owningWidget);if(!owningWindow)returnNS_ERROR_FAILURE;if(aRaise){// aRaise == true means request toplevel activation.// This is asynchronous.// If and when the window manager accepts the request, then the focus// widget will get a focus-in-event signal.if(gRaiseWindows&&owningWindow->mIsShown&&owningWindow->mShell&&!gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))){uint32_ttimestamp=GDK_CURRENT_TIME;nsGTKToolkit*GTKToolkit=nsGTKToolkit::GetToolkit();if(GTKToolkit)timestamp=GTKToolkit->GetFocusTimestamp();LOGFOCUS((" requesting toplevel activation [%p]\n",(void*)this));NS_ASSERTION(owningWindow->mWindowType!=eWindowType_popup||mParent,"Presenting an override-redirect window");gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell),timestamp);if(GTKToolkit)GTKToolkit->SetFocusTimestamp(0);}returnNS_OK;}// aRaise == false means that keyboard events should be dispatched// from this widget.// Ensure owningWidget is the focused GtkWidget within its toplevel window.//// For eWindowType_popup, this GtkWidget may not actually be the one that// receives the key events as it may be the parent window that is active.if(!gtk_widget_is_focus(owningWidget)){// This is synchronous. It takes focus from a plugin or from a widget// in an embedder. The focus manager already knows that this window// is active so gBlockActivateEvent avoids another (unnecessary)// activate notification.gBlockActivateEvent=true;gtk_widget_grab_focus(owningWidget);gBlockActivateEvent=false;}// If this is the widget that already has focus, return.if(gFocusWindow==this){LOGFOCUS((" already have focus [%p]\n",(void*)this));returnNS_OK;}// Set this window to be the focused child windowgFocusWindow=this;if(mIMContext){mIMContext->OnFocusWindow(this);}LOGFOCUS((" widget now has focus in SetFocus() [%p]\n",(void*)this));returnNS_OK;}LayoutDeviceIntRectnsWindow::GetScreenBounds(){LayoutDeviceIntRectrect;if(mIsTopLevel&&mContainer){// use the point including window decorationsgintx,y;gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)),&x,&y);rect.MoveTo(GdkPointToDevicePixels({x,y}));}else{rect.MoveTo(WidgetToScreenOffset());}// mBounds.Size() is the window bounds, not the window-manager frame// bounds (bug 581863). gdk_window_get_frame_extents would give the// frame bounds, but mBounds.Size() is returned here for consistency// with Resize.rect.SizeTo(mBounds.Size());LOG(("GetScreenBounds %d,%d | %dx%d\n",rect.x,rect.y,rect.width,rect.height));returnrect;}LayoutDeviceIntSizensWindow::GetClientSize(){returnLayoutDeviceIntSize(mBounds.width,mBounds.height);}LayoutDeviceIntRectnsWindow::GetClientBounds(){// GetBounds returns a rect whose top left represents the top left of the// outer bounds, but whose width/height represent the size of the inner// bounds (which is messed up).LayoutDeviceIntRectrect=GetBounds();rect.MoveBy(GetClientOffset());returnrect;}voidnsWindow::UpdateClientOffset(){AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset",GRAPHICS);if(!mIsTopLevel||!mShell||!mGdkWindow||!mIsX11Display||gtk_window_get_window_type(GTK_WINDOW(mShell))==GTK_WINDOW_POPUP){mClientOffset=nsIntPoint(0,0);return;}GdkAtomcardinal_atom=gdk_x11_xatom_to_atom(XA_CARDINAL);GdkAtomtype_returned;intformat_returned;intlength_returned;long*frame_extents;if(!gdk_property_get(mGdkWindow,gdk_atom_intern("_NET_FRAME_EXTENTS",FALSE),cardinal_atom,0,// offset4*4,// lengthFALSE,// delete&type_returned,&format_returned,&length_returned,(guchar**)&frame_extents)||length_returned/sizeof(glong)!=4){mClientOffset=nsIntPoint(0,0);return;}// data returned is in the order left, right, top, bottomautoleft=int32_t(frame_extents[0]);autotop=int32_t(frame_extents[2]);g_free(frame_extents);mClientOffset=nsIntPoint(left,top);}LayoutDeviceIntPointnsWindow::GetClientOffset(){returnLayoutDeviceIntPoint::FromUnknownPoint(mClientOffset);}gbooleannsWindow::OnPropertyNotifyEvent(GtkWidget*aWidget,GdkEventProperty*aEvent){if(aEvent->atom==gdk_atom_intern("_NET_FRAME_EXTENTS",FALSE)){UpdateClientOffset();returnFALSE;}if(GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget,aEvent)){returnTRUE;}returnFALSE;}voidnsWindow::SetCursor(nsCursoraCursor){// if we're not the toplevel window pass up the cursor request to// the toplevel window to handle it.if(!mContainer&&mGdkWindow){nsWindow*window=GetContainerWindow();if(!window)return;window->SetCursor(aCursor);return;}// Only change cursor if it's actually been changedif(aCursor!=mCursor||mUpdateCursor){GdkCursor*newCursor=nullptr;mUpdateCursor=false;newCursor=get_gtk_cursor(aCursor);if(nullptr!=newCursor){mCursor=aCursor;if(!mContainer)return;gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),newCursor);}}}nsresultnsWindow::SetCursor(imgIContainer*aCursor,uint32_taHotspotX,uint32_taHotspotY){// if we're not the toplevel window pass up the cursor request to// the toplevel window to handle it.if(!mContainer&&mGdkWindow){nsWindow*window=GetContainerWindow();if(!window)returnNS_ERROR_FAILURE;returnwindow->SetCursor(aCursor,aHotspotX,aHotspotY);}mCursor=nsCursor(-1);// Get the image's current frameGdkPixbuf*pixbuf=nsImageToPixbuf::ImageToPixbuf(aCursor);if(!pixbuf)returnNS_ERROR_NOT_AVAILABLE;intwidth=gdk_pixbuf_get_width(pixbuf);intheight=gdk_pixbuf_get_height(pixbuf);// Reject cursors greater than 128 pixels in some direction, to prevent// spoofing.// XXX ideally we should rescale. Also, we could modify the API to// allow trusted content to set larger cursors.if(width>128||height>128){g_object_unref(pixbuf);returnNS_ERROR_NOT_AVAILABLE;}// Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This// is of course not documented anywhere...// So add one if there isn't one yetif(!gdk_pixbuf_get_has_alpha(pixbuf)){GdkPixbuf*alphaBuf=gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);g_object_unref(pixbuf);if(!alphaBuf){returnNS_ERROR_OUT_OF_MEMORY;}pixbuf=alphaBuf;}GdkCursor*cursor=gdk_cursor_new_from_pixbuf(gdk_display_get_default(),pixbuf,aHotspotX,aHotspotY);g_object_unref(pixbuf);nsresultrv=NS_ERROR_OUT_OF_MEMORY;if(cursor){if(mContainer){gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),cursor);rv=NS_OK;}#if (MOZ_WIDGET_GTK == 3)g_object_unref(cursor);#elsegdk_cursor_unref(cursor);#endif}returnrv;}voidnsWindow::Invalidate(constLayoutDeviceIntRect&aRect){if(!mGdkWindow)return;GdkRectanglerect=DevicePixelsToGdkRectRoundOut(aRect);gdk_window_invalidate_rect(mGdkWindow,&rect,FALSE);LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n",(void*)this,rect.x,rect.y,rect.width,rect.height));}void*nsWindow::GetNativeData(uint32_taDataType){switch(aDataType){caseNS_NATIVE_WINDOW:caseNS_NATIVE_WIDGET:{if(!mGdkWindow)returnnullptr;returnmGdkWindow;}caseNS_NATIVE_DISPLAY:{#ifdef MOZ_X11GdkDisplay*gdkDisplay=gdk_display_get_default();if(GDK_IS_X11_DISPLAY(gdkDisplay)){returnGDK_DISPLAY_XDISPLAY(gdkDisplay);}#endif /* MOZ_X11 */returnnullptr;}caseNS_NATIVE_SHELLWIDGET:returnGetToplevelWidget();caseNS_NATIVE_SHAREABLE_WINDOW:return(void*)GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));caseNS_RAW_NATIVE_IME_CONTEXT:{void*pseudoIMEContext=GetPseudoIMEContext();if(pseudoIMEContext){returnpseudoIMEContext;}// If IME context isn't available on this widget, we should set |this|// instead of nullptr.if(!mIMContext){returnthis;}returnmIMContext.get();}caseNS_NATIVE_OPENGL_CONTEXT:returnnullptr;#ifdef MOZ_X11caseNS_NATIVE_COMPOSITOR_DISPLAY:returngfxPlatformGtk::GetPlatform()->GetCompositorDisplay();#endif // MOZ_X11default:NS_WARNING("nsWindow::GetNativeData called with bad value");returnnullptr;}}nsresultnsWindow::SetTitle(constnsAString&aTitle){if(!mShell)returnNS_OK;// convert the string into utf8 and set the title.#define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)NS_ConvertUTF16toUTF8titleUTF8(aTitle);if(titleUTF8.Length()>NS_WINDOW_TITLE_MAX_LENGTH){// Truncate overlong titles (bug 167315). Make sure we chop after a// complete sequence by making sure the next char isn't a follow-byte.uint32_tlen=NS_WINDOW_TITLE_MAX_LENGTH;while(UTF8_FOLLOWBYTE(titleUTF8[len]))--len;titleUTF8.Truncate(len);}gtk_window_set_title(GTK_WINDOW(mShell),(constchar*)titleUTF8.get());returnNS_OK;}voidnsWindow::SetIcon(constnsAString&aIconSpec){if(!mShell)return;nsAutoCStringiconName;if(aIconSpec.EqualsLiteral("default")){nsXPIDLStringbrandName;GetBrandName(brandName);AppendUTF16toUTF8(brandName,iconName);ToLowerCase(iconName);}else{AppendUTF16toUTF8(aIconSpec,iconName);}nsCOMPtr<nsIFile>iconFile;nsAutoCStringpath;gint*iconSizes=gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),iconName.get());boolfoundIcon=(iconSizes[0]!=0);g_free(iconSizes);if(!foundIcon){// Look for icons with the following suffixes appended to the base name// The last two entries (for the old XPM format) will be ignored unless// no icons are found using other suffixes. XPM icons are deprecated.constcharextensions[6][7]={".png","16.png","32.png","48.png",".xpm","16.xpm"};for(uint32_ti=0;i<ArrayLength(extensions);i++){// Don't bother looking for XPM versions if we found a PNG.if(i==ArrayLength(extensions)-2&&foundIcon)break;nsAutoStringextension;extension.AppendASCII(extensions[i]);ResolveIconName(aIconSpec,extension,getter_AddRefs(iconFile));if(iconFile){iconFile->GetNativePath(path);GdkPixbuf*icon=gdk_pixbuf_new_from_file(path.get(),nullptr);if(icon){gtk_icon_theme_add_builtin_icon(iconName.get(),gdk_pixbuf_get_height(icon),icon);g_object_unref(icon);foundIcon=true;}}}}// leave the default icon intact if no matching icons were foundif(foundIcon){gtk_window_set_icon_name(GTK_WINDOW(mShell),iconName.get());}}LayoutDeviceIntPointnsWindow::WidgetToScreenOffset(){gintx=0,y=0;if(mGdkWindow){gdk_window_get_origin(mGdkWindow,&x,&y);}returnGdkPointToDevicePixels({x,y});}voidnsWindow::CaptureMouse(boolaCapture){LOG(("CaptureMouse %p\n",(void*)this));if(!mGdkWindow)return;if(!mContainer)return;if(aCapture){gtk_grab_add(GTK_WIDGET(mContainer));GrabPointer(GetLastUserInputTime());}else{ReleaseGrabs();gtk_grab_remove(GTK_WIDGET(mContainer));}}voidnsWindow::CaptureRollupEvents(nsIRollupListener*aListener,boolaDoCapture){if(!mGdkWindow)return;if(!mContainer)return;LOG(("CaptureRollupEvents %p %i\n",this,int(aDoCapture)));if(aDoCapture){gRollupListener=aListener;// Don't add a grab if a drag is in progress, or if the widget is a drag// feedback popup. (panels with type="drag").if(!mIsDragPopup&&!nsWindow::DragInProgress()){gtk_grab_add(GTK_WIDGET(mContainer));GrabPointer(GetLastUserInputTime());}}else{if(!nsWindow::DragInProgress()){ReleaseGrabs();}// There may not have been a drag in process when aDoCapture was set,// so make sure to remove any added grab. This is a no-op if the grab// was not added to this widget.gtk_grab_remove(GTK_WIDGET(mContainer));gRollupListener=nullptr;}}nsresultnsWindow::GetAttention(int32_taCycleCount){LOG(("nsWindow::GetAttention [%p]\n",(void*)this));GtkWidget*top_window=GetToplevelWidget();GtkWidget*top_focused_window=gFocusWindow?gFocusWindow->GetToplevelWidget():nullptr;// Don't get attention if the window is focused anyway.if(top_window&&(gtk_widget_get_visible(top_window))&&top_window!=top_focused_window){SetUrgencyHint(top_window,true);}returnNS_OK;}boolnsWindow::HasPendingInputEvent(){// This sucks, but gtk/gdk has no way to answer the question we want while// excluding paint events, and there's no X API that will let us peek// without blocking or removing. To prevent event reordering, peek// anything except expose events. Reordering expose and others should be// ok, hopefully.boolhaveEvent=false;#ifdef MOZ_X11XEventev;if(mIsX11Display){Display*display=GDK_DISPLAY_XDISPLAY(gdk_display_get_default());haveEvent=XCheckMaskEvent(display,KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask|PointerMotionMask|PointerMotionHintMask|Button1MotionMask|Button2MotionMask|Button3MotionMask|Button4MotionMask|Button5MotionMask|ButtonMotionMask|KeymapStateMask|VisibilityChangeMask|StructureNotifyMask|ResizeRedirectMask|SubstructureNotifyMask|SubstructureRedirectMask|FocusChangeMask|PropertyChangeMask|ColormapChangeMask|OwnerGrabButtonMask,&ev);if(haveEvent){XPutBackEvent(display,&ev);}}#endifreturnhaveEvent;}#if 0#ifdef DEBUG// Paint flashing code (disabled for cairo - see below)#define CAPS_LOCK_IS_ON \(KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))#define WANT_PAINT_FLASHING \(debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)#ifdef MOZ_X11static voidgdk_window_flash(GdkWindow * aGdkWindow, unsigned int aTimes, unsigned int aInterval, // Milliseconds GdkRegion * aRegion){ gint x; gint y; gint width; gint height; guint i; GdkGC * gc = 0; GdkColor white;#if (MOZ_WIDGET_GTK == 2) gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr);#else gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);#endif gdk_window_get_origin (aGdkWindow, &x, &y); gc = gdk_gc_new(gdk_get_default_root_window()); white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display)); gdk_gc_set_foreground(gc,&white); gdk_gc_set_function(gc,GDK_XOR); gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS); gdk_region_offset(aRegion, x, y); gdk_gc_set_clip_region(gc, aRegion); /* * Need to do this twice so that the XOR effect can replace * the original window contents. */ for (i = 0; i < aTimes * 2; i++) { gdk_draw_rectangle(gdk_get_default_root_window(), gc, TRUE, x, y, width, height); gdk_flush(); PR_Sleep(PR_MillisecondsToInterval(aInterval)); } gdk_gc_destroy(gc); gdk_region_offset(aRegion, -x, -y);}#endif /* MOZ_X11 */#endif // DEBUG#endif#if (MOZ_WIDGET_GTK == 2)staticboolExtractExposeRegion(LayoutDeviceIntRegion&aRegion,GdkEventExpose*aEvent){GdkRectangle*rects;gintnrects;gdk_region_get_rectangles(aEvent->region,&rects,&nrects);if(nrects>MAX_RECTS_IN_REGION){// Just use the bounding boxrects[0]=aEvent->area;nrects=1;}for(GdkRectangle*r=rects;r<rects+nrects;r++){aRegion.Or(aRegion,LayoutDeviceIntRect(r->x,r->y,r->width,r->height));LOGDRAW(("\t%d %d %d %d\n",r->x,r->y,r->width,r->height));}g_free(rects);returntrue;}#else# ifdef cairo_copy_clip_rectangle_list# error "Looks like we're including Mozilla's cairo instead of system cairo"# endifstaticboolExtractExposeRegion(LayoutDeviceIntRegion&aRegion,cairo_t*cr){cairo_rectangle_list_t*rects=cairo_copy_clip_rectangle_list(cr);if(rects->status!=CAIRO_STATUS_SUCCESS){NS_WARNING("Failed to obtain cairo rectangle list.");returnfalse;}for(inti=0;i<rects->num_rectangles;i++){constcairo_rectangle_t&r=rects->rectangles[i];aRegion.Or(aRegion,LayoutDeviceIntRect::Truncate(r.x,r.y,r.width,r.height));LOGDRAW(("\t%f %f %f %f\n",r.x,r.y,r.width,r.height));}cairo_rectangle_list_destroy(rects);returntrue;}#endif#if (MOZ_WIDGET_GTK == 2)gbooleannsWindow::OnExposeEvent(GdkEventExpose*aEvent)#elsegbooleannsWindow::OnExposeEvent(cairo_t*cr)#endif{// Send any pending resize events so that layout can update.// May run event loop.MaybeDispatchResized();if(mIsDestroyed){returnFALSE;}// Windows that are not visible will be painted after they become visible.if(!mGdkWindow||mIsFullyObscured||!mHasMappedToplevel)returnFALSE;nsIWidgetListener*listener=GetListener();if(!listener)returnFALSE;LayoutDeviceIntRegionexposeRegion;#if (MOZ_WIDGET_GTK == 2)if(!ExtractExposeRegion(exposeRegion,aEvent)){#elseif(!ExtractExposeRegion(exposeRegion,cr)){#endifreturnFALSE;}gintscale=GdkScaleFactor();LayoutDeviceIntRegionregion=exposeRegion;region.ScaleRoundOut(scale,scale);if(GetLayerManager()->AsKnowsCompositor()&&mCompositorSession){// We need to paint to the screen even if nothing changed, since if we// don't have a compositing window manager, our pixels could be stale.GetLayerManager()->SetNeedsComposite(true);GetLayerManager()->SendInvalidRegion(region.ToUnknownRegion());}RefPtr<nsWindow>strongThis(this);// Dispatch WillPaintWindow notification to allow scripts etc. to run// before we paint{listener->WillPaintWindow(this);// If the window has been destroyed during the will paint notification,// there is nothing left to do.if(!mGdkWindow)returnTRUE;// Re-get the listener since the will paint notification might have// killed it.listener=GetListener();if(!listener)returnFALSE;}if(GetLayerManager()->AsKnowsCompositor()&&GetLayerManager()->NeedsComposite()){GetLayerManager()->ScheduleComposite();GetLayerManager()->SetNeedsComposite(false);}LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",(void*)this,(void*)mGdkWindow,gdk_x11_window_get_xid(mGdkWindow)));// Our bounds may have changed after calling WillPaintWindow. Clip// to the new bounds here. The region is relative to this// window.region.And(region,LayoutDeviceIntRect(0,0,mBounds.width,mBounds.height));boolshaped=false;if(eTransparencyTransparent==GetTransparencyMode()){GdkScreen*screen=gdk_window_get_screen(mGdkWindow);if(gdk_screen_is_composited(screen)&&gdk_window_get_visual(mGdkWindow)==gdk_screen_get_rgba_visual(screen)){// Remove possible shape mask from when window manger was not// previously compositing.static_cast<nsWindow*>(GetTopLevelWidget())->ClearTransparencyBitmap();}else{shaped=true;}}if(!shaped){GList*children=gdk_window_peek_children(mGdkWindow);while(children){GdkWindow*gdkWin=GDK_WINDOW(children->data);nsWindow*kid=get_window_for_gdk_window(gdkWin);if(kid&&gdk_window_is_visible(gdkWin)){AutoTArray<LayoutDeviceIntRect,1>clipRects;kid->GetWindowClipRegion(&clipRects);LayoutDeviceIntRectbounds=kid->GetBounds();for(uint32_ti=0;i<clipRects.Length();++i){LayoutDeviceIntRectr=clipRects[i]+bounds.TopLeft();region.Sub(region,r);}}children=children->next;}}if(region.IsEmpty()){returnTRUE;}// If this widget uses OMTC...if(GetLayerManager()->GetBackendType()==LayersBackend::LAYERS_CLIENT||GetLayerManager()->GetBackendType()==LayersBackend::LAYERS_WR){listener->PaintWindow(this,region);// Re-get the listener since the will paint notification might have// killed it.listener=GetListener();if(!listener)returnTRUE;listener->DidPaintWindow();returnTRUE;}BufferModelayerBuffering=BufferMode::BUFFERED;RefPtr<DrawTarget>dt=StartRemoteDrawingInRegion(region,&layerBuffering);if(!dt||!dt->IsValid()){returnFALSE;}RefPtr<gfxContext>ctx;IntRectboundsRect=region.GetBounds().ToUnknownRect();IntPointoffset(0,0);if(dt->GetSize()==boundsRect.Size()){offset=boundsRect.TopLeft();dt->SetTransform(Matrix::Translation(-offset));}#ifdef MOZ_X11if(shaped){// Collapse update area to the bounding box. This is so we only have to// call UpdateTranslucentWindowAlpha once. After we have dropped// support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be// our private interface so we can rework things to avoid this.dt->PushClipRect(Rect(boundsRect));// The double buffering is done here to extract the shape mask.// (The shape mask won't be necessary when a visual with an alpha// channel is used on compositing window managers.)layerBuffering=BufferMode::BUFFER_NONE;RefPtr<DrawTarget>destDT=dt->CreateSimilarDrawTarget(boundsRect.Size(),SurfaceFormat::B8G8R8A8);if(!destDT||!destDT->IsValid()){returnFALSE;}destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft()));ctx=gfxContext::CreatePreservingTransformOrNull(destDT);}else{gfxUtils::ClipToRegion(dt,region.ToUnknownRegion());ctx=gfxContext::CreatePreservingTransformOrNull(dt);}MOZ_ASSERT(ctx);// checked both dt and destDT valid draw target above#if 0 // NOTE: Paint flashing region would be wrong for cairo, since // cairo inflates the update region, etc. So don't paint flash // for cairo.#ifdef DEBUG // XXX aEvent->region may refer to a newly-invalid area. FIXME if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent)) gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);#endif#endif#endif // MOZ_X11boolpainted=false;{if(GetLayerManager()->GetBackendType()==LayersBackend::LAYERS_BASIC){GdkScreen*screen=gdk_window_get_screen(mGdkWindow);if(GetTransparencyMode()==eTransparencyTransparent&&layerBuffering==BufferMode::BUFFER_NONE&&gdk_screen_is_composited(screen)&&gdk_window_get_visual(mGdkWindow)==gdk_screen_get_rgba_visual(screen)){// If our draw target is unbuffered and we use an alpha channel,// clear the image beforehand to ensure we don't get artifacts from a// reused SHM image. See bug 1258086.dt->ClearRect(Rect(boundsRect));}AutoLayerManagerSetupsetupLayerManager(this,ctx,layerBuffering);painted=listener->PaintWindow(this,region);// Re-get the listener since the will paint notification might have// killed it.listener=GetListener();if(!listener)returnTRUE;}}#ifdef MOZ_X11// PaintWindow can Destroy us (bug 378273), avoid doing any paint// operations below if that happened - it will lead to XError and exit().if(shaped){if(MOZ_LIKELY(!mIsDestroyed)){if(painted){RefPtr<SourceSurface>surf=ctx->GetDrawTarget()->Snapshot();UpdateAlpha(surf,boundsRect);dt->DrawSurface(surf,Rect(boundsRect),Rect(0,0,boundsRect.width,boundsRect.height),DrawSurfaceOptions(SamplingFilter::POINT),DrawOptions(1.0f,CompositionOp::OP_SOURCE));}}}ctx=nullptr;dt->PopClip();#endif // MOZ_X11EndRemoteDrawingInRegion(dt,region);listener->DidPaintWindow();// Synchronously flush any new dirty areas#if (MOZ_WIDGET_GTK == 2)GdkRegion*dirtyArea=gdk_window_get_update_area(mGdkWindow);#elsecairo_region_t*dirtyArea=gdk_window_get_update_area(mGdkWindow);#endifif(dirtyArea){gdk_window_invalidate_region(mGdkWindow,dirtyArea,false);#if (MOZ_WIDGET_GTK == 2)gdk_region_destroy(dirtyArea);#elsecairo_region_destroy(dirtyArea);#endifgdk_window_process_updates(mGdkWindow,false);}// check the return value!returnTRUE;}voidnsWindow::UpdateAlpha(SourceSurface*aSourceSurface,nsIntRectaBoundsRect){// We need to create our own buffer to force the stride to match the// expected stride.int32_tstride=GetAlignedStride<4>(aBoundsRect.width,BytesPerPixel(SurfaceFormat::A8));if(stride==0){return;}int32_tbufferSize=stride*aBoundsRect.height;autoimageBuffer=MakeUniqueFallible<uint8_t[]>(bufferSize);{RefPtr<DrawTarget>drawTarget=gfxPlatform::CreateDrawTargetForData(imageBuffer.get(),aBoundsRect.Size(),stride,SurfaceFormat::A8);if(drawTarget){drawTarget->DrawSurface(aSourceSurface,Rect(0,0,aBoundsRect.width,aBoundsRect.height),Rect(0,0,aSourceSurface->GetSize().width,aSourceSurface->GetSize().height),DrawSurfaceOptions(SamplingFilter::POINT),DrawOptions(1.0f,CompositionOp::OP_SOURCE));}}UpdateTranslucentWindowAlphaInternal(aBoundsRect,imageBuffer.get(),stride);}gbooleannsWindow::OnConfigureEvent(GtkWidget*aWidget,GdkEventConfigure*aEvent){// These events are only received on toplevel windows.//// GDK ensures that the coordinates are the client window top-left wrt the// root window.//// GDK calculates the cordinates for real ConfigureNotify events on// managed windows (that would normally be relative to the parent// window).//// Synthetic ConfigureNotify events are from the window manager and// already relative to the root window. GDK creates all X windows with// border_width = 0, so synthetic events also indicate the top-left of// the client window.//// Override-redirect windows are children of the root window so parent// coordinates are root coordinates.LOG(("configure event [%p] %d %d %d %d\n",(void*)this,aEvent->x,aEvent->y,aEvent->width,aEvent->height));if(mPendingConfigures>0){mPendingConfigures--;}LayoutDeviceIntRectscreenBounds=GetScreenBounds();if(mWindowType==eWindowType_toplevel||mWindowType==eWindowType_dialog){// This check avoids unwanted rollup on spurious configure events from// Cygwin/X (bug 672103).if(mBounds.x!=screenBounds.x||mBounds.y!=screenBounds.y){CheckForRollup(0,0,false,true);}}// This event indicates that the window position may have changed.// mBounds.Size() is updated in OnSizeAllocate().NS_ASSERTION(GTK_IS_WINDOW(aWidget),"Configure event on widget that is not a GtkWindow");if(gtk_window_get_window_type(GTK_WINDOW(aWidget))==GTK_WINDOW_POPUP){// Override-redirect window//// These windows should not be moved by the window manager, and so any// change in position is a result of our direction. mBounds has// already been set in Move() or Resize(), and that is more// up-to-date than the position in the ConfigureNotify event if the// event is from an earlier window move.//// Skipping the WindowMoved call saves context menus from an infinite// loop when nsXULPopupManager::PopupMoved moves the window to the new// position and nsMenuPopupFrame::SetPopupPosition adds// offsetForContextMenu on each iteration.returnFALSE;}mBounds.MoveTo(screenBounds.TopLeft());// XXX mozilla will invalidate the entire window after this move// complete. wtf?NotifyWindowMoved(mBounds.x,mBounds.y);returnFALSE;}voidnsWindow::OnContainerUnrealize(){// The GdkWindows are about to be destroyed (but not deleted), so remove// their references back to their container widget while the GdkWindow// hierarchy is still available.if(mGdkWindow){DestroyChildWindows();g_object_set_data(G_OBJECT(mGdkWindow),"nsWindow",nullptr);mGdkWindow=nullptr;}}voidnsWindow::OnSizeAllocate(GtkAllocation*aAllocation){LOG(("size_allocate [%p] %d %d %d %d\n",(void*)this,aAllocation->x,aAllocation->y,aAllocation->width,aAllocation->height));LayoutDeviceIntSizesize=GdkRectToDevicePixels(*aAllocation).Size();if(mBounds.Size()==size)return;// Invalidate the new part of the window now for the pending paint to// minimize background flashes (GDK does not do this for external resizes// of toplevels.)if(mBounds.width<size.width){GdkRectanglerect=DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(mBounds.width,0,size.width-mBounds.width,size.height));gdk_window_invalidate_rect(mGdkWindow,&rect,FALSE);}if(mBounds.height<size.height){GdkRectanglerect=DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(0,mBounds.height,size.width,size.height-mBounds.height));gdk_window_invalidate_rect(mGdkWindow,&rect,FALSE);}mBounds.SizeTo(size);#ifdef MOZ_X11// Notify the X11CompositorWidget of a ClientSizeChangeif(mCompositorWidgetDelegate){mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());}#endif// Gecko permits running nested event loops during processing of events,// GtkWindow callers of gtk_widget_size_allocate expect the signal// handlers to return sometime in the near future.mNeedsDispatchResized=true;NS_DispatchToCurrentThread(NewRunnableMethod("nsWindow::MaybeDispatchResized",this,&nsWindow::MaybeDispatchResized));}voidnsWindow::OnDeleteEvent(){if(mWidgetListener)mWidgetListener->RequestWindowClose(this);}voidnsWindow::OnEnterNotifyEvent(GdkEventCrossing*aEvent){// This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events// when the pointer enters a child window. If the destination window is a// Gecko window then we'll catch the corresponding event on that window,// but we won't notice when the pointer directly enters a foreign (plugin)// child window without passing over a visible portion of a Gecko window.if(aEvent->subwindow!=nullptr)return;// Check before is_parent_ungrab_enter() as the button state may have// changed while a non-Gecko ancestor window had a pointer grab.DispatchMissedButtonReleases(aEvent);if(is_parent_ungrab_enter(aEvent))return;WidgetMouseEventevent(true,eMouseEnterIntoWidget,this,WidgetMouseEvent::eReal);event.mRefPoint=GdkEventCoordsToDevicePixels(aEvent->x,aEvent->y);event.AssignEventTime(GetWidgetEventTime(aEvent->time));LOG(("OnEnterNotify: %p\n",(void*)this));DispatchInputEvent(&event);}// XXX Is this the right test for embedding cases?staticboolis_top_level_mouse_exit(GdkWindow*aWindow,GdkEventCrossing*aEvent){autox=gint(aEvent->x_root);autoy=gint(aEvent->y_root);GdkDisplay*display=gdk_window_get_display(aWindow);GdkWindow*winAtPt=gdk_display_get_window_at_pointer(display,&x,&y);if(!winAtPt)returntrue;GdkWindow*topLevelAtPt=gdk_window_get_toplevel(winAtPt);GdkWindow*topLevelWidget=gdk_window_get_toplevel(aWindow);returntopLevelAtPt!=topLevelWidget;}voidnsWindow::OnLeaveNotifyEvent(GdkEventCrossing*aEvent){// This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify// events when the pointer leaves a child window. If the destination// window is a Gecko window then we'll catch the corresponding event on// that window.//// XXXkt However, we will miss toplevel exits when the pointer directly// leaves a foreign (plugin) child window without passing over a visible// portion of a Gecko window.if(aEvent->subwindow!=nullptr)return;WidgetMouseEventevent(true,eMouseExitFromWidget,this,WidgetMouseEvent::eReal);event.mRefPoint=GdkEventCoordsToDevicePixels(aEvent->x,aEvent->y);event.AssignEventTime(GetWidgetEventTime(aEvent->time));event.mExitFrom=is_top_level_mouse_exit(mGdkWindow,aEvent)?WidgetMouseEvent::eTopLevel:WidgetMouseEvent::eChild;LOG(("OnLeaveNotify: %p\n",(void*)this));DispatchInputEvent(&event);}template<typenameEvent>staticLayoutDeviceIntPointGetRefPoint(nsWindow*aWindow,Event*aEvent){if(aEvent->window==aWindow->GetGdkWindow()){// we are the window that the event happened on so no need for expensive WidgetToScreenOffsetreturnaWindow->GdkEventCoordsToDevicePixels(aEvent->x,aEvent->y);}// XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling// in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into// coordinates relative to this widget.returnaWindow->GdkEventCoordsToDevicePixels(aEvent->x_root,aEvent->y_root)-aWindow->WidgetToScreenOffset();}voidnsWindow::OnMotionNotifyEvent(GdkEventMotion*aEvent){// see if we can compress this event// XXXldb Why skip every other motion event when we have multiple,// but not more than that?boolsynthEvent=false;#ifdef MOZ_X11XEventxevent;if(mIsX11Display){while(XPending(GDK_WINDOW_XDISPLAY(aEvent->window))){XEventpeeked;XPeekEvent(GDK_WINDOW_XDISPLAY(aEvent->window),&peeked);if(peeked.xany.window!=gdk_x11_window_get_xid(aEvent->window)||peeked.type!=MotionNotify)break;synthEvent=true;XNextEvent(GDK_WINDOW_XDISPLAY(aEvent->window),&xevent);}}#endif /* MOZ_X11 */WidgetMouseEventevent(true,eMouseMove,this,WidgetMouseEvent::eReal);gdoublepressure=0;gdk_event_get_axis((GdkEvent*)aEvent,GDK_AXIS_PRESSURE,&pressure);// Sometime gdk generate 0 pressure value between normal values// We have to ignore that and use last valid valueif(pressure)mLastMotionPressure=pressure;event.pressure=mLastMotionPressure;guintmodifierState;if(synthEvent){#ifdef MOZ_X11event.mRefPoint.x=nscoord(xevent.xmotion.x);event.mRefPoint.y=nscoord(xevent.xmotion.y);modifierState=xevent.xmotion.state;event.AssignEventTime(GetWidgetEventTime(xevent.xmotion.time));#elseevent.mRefPoint=GdkEventCoordsToDevicePixels(aEvent->x,aEvent->y);modifierState=aEvent->state;event.AssignEventTime(GetWidgetEventTime(aEvent->time));#endif /* MOZ_X11 */}else{event.mRefPoint=GetRefPoint(this,aEvent);modifierState=aEvent->state;event.AssignEventTime(GetWidgetEventTime(aEvent->time));}KeymapWrapper::InitInputEvent(event,modifierState);DispatchInputEvent(&event);}// If the automatic pointer grab on ButtonPress has deactivated before// ButtonRelease, and the mouse button is released while the pointer is not// over any a Gecko window, then the ButtonRelease event will not be received.// (A similar situation exists when the pointer is grabbed with owner_events// True as the ButtonRelease may be received on a foreign [plugin] window).// Use this method to check for released buttons when the pointer returns to a// Gecko window.voidnsWindow::DispatchMissedButtonReleases(GdkEventCrossing*aGdkEvent){guintchanged=aGdkEvent->state^gButtonState;// Only consider button releases.// (Ignore button presses that occurred outside Gecko.)guintreleased=changed&gButtonState;gButtonState=aGdkEvent->state;// Loop over each button, excluding mouse wheel buttons 4 and 5 for which// GDK ignores releases.for(guintbuttonMask=GDK_BUTTON1_MASK;buttonMask<=GDK_BUTTON3_MASK;buttonMask<<=1){if(released&buttonMask){int16_tbuttonType;switch(buttonMask){caseGDK_BUTTON1_MASK:buttonType=WidgetMouseEvent::eLeftButton;break;caseGDK_BUTTON2_MASK:buttonType=WidgetMouseEvent::eMiddleButton;break;default:NS_ASSERTION(buttonMask==GDK_BUTTON3_MASK,"Unexpected button mask");buttonType=WidgetMouseEvent::eRightButton;}LOG(("Synthesized button %u release on %p\n",guint(buttonType+1),(void*)this));// Dispatch a synthesized button up event to tell Gecko about the// change in state. This event is marked as synthesized so that// it is not dispatched as a DOM event, because we don't know the// position, widget, modifiers, or time/order.WidgetMouseEventsynthEvent(true,eMouseUp,this,WidgetMouseEvent::eSynthesized);synthEvent.button=buttonType;DispatchInputEvent(&synthEvent);}}}voidnsWindow::InitButtonEvent(WidgetMouseEvent&aEvent,GdkEventButton*aGdkEvent){aEvent.mRefPoint=GetRefPoint(this,aGdkEvent);guintmodifierState=aGdkEvent->state;// aEvent's state includes the button state from immediately before this// event. If aEvent is a mousedown or mouseup event, we need to update// the button state.guintbuttonMask=0;switch(aGdkEvent->button){case1:buttonMask=GDK_BUTTON1_MASK;break;case2:buttonMask=GDK_BUTTON2_MASK;break;case3:buttonMask=GDK_BUTTON3_MASK;break;}if(aGdkEvent->type==GDK_BUTTON_RELEASE){modifierState&=~buttonMask;}else{modifierState|=buttonMask;}KeymapWrapper::InitInputEvent(aEvent,modifierState);aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time));switch(aGdkEvent->type){caseGDK_2BUTTON_PRESS:aEvent.mClickCount=2;break;caseGDK_3BUTTON_PRESS:aEvent.mClickCount=3;break;// default is one clickdefault:aEvent.mClickCount=1;}}staticguintButtonMaskFromGDKButton(guintbutton){returnGDK_BUTTON1_MASK<<(button-1);}voidnsWindow::OnButtonPressEvent(GdkEventButton*aEvent){LOG(("Button %u press on %p\n",aEvent->button,(void*)this));// If you double click in GDK, it will actually generate a second// GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is// different than the DOM spec. GDK puts this in the queue// programatically, so it's safe to assume that if there's a// double click in the queue, it was generated so we can just drop// this click.GdkEvent*peekedEvent=gdk_event_peek();if(peekedEvent){GdkEventTypetype=peekedEvent->any.type;gdk_event_free(peekedEvent);if(type==GDK_2BUTTON_PRESS||type==GDK_3BUTTON_PRESS)return;}nsWindow*containerWindow=GetContainerWindow();if(!gFocusWindow&&containerWindow){containerWindow->DispatchActivateEvent();}// check to see if we should rollupif(CheckForRollup(aEvent->x_root,aEvent->y_root,false,false))return;gdoublepressure=0;gdk_event_get_axis((GdkEvent*)aEvent,GDK_AXIS_PRESSURE,&pressure);mLastMotionPressure=pressure;uint16_tdomButton;switch(aEvent->button){case1:domButton=WidgetMouseEvent::eLeftButton;break;case2:domButton=WidgetMouseEvent::eMiddleButton;break;case3:domButton=WidgetMouseEvent::eRightButton;break;// These are mapped to horizontal scrollcase6:case7:NS_WARNING("We're not supporting legacy horizontal scroll event");return;// Map buttons 8-9 to back/forwardcase8:DispatchCommandEvent(nsGkAtoms::Back);return;case9:DispatchCommandEvent(nsGkAtoms::Forward);return;default:return;}gButtonState|=ButtonMaskFromGDKButton(aEvent->button);WidgetMouseEventevent(true,eMouseDown,this,WidgetMouseEvent::eReal);event.button=domButton;InitButtonEvent(event,aEvent);event.pressure=mLastMotionPressure;DispatchInputEvent(&event);// right menu click on linux should also pop up a context menuif(domButton==WidgetMouseEvent::eRightButton&&MOZ_LIKELY(!mIsDestroyed)){WidgetMouseEventcontextMenuEvent(true,eContextMenu,this,WidgetMouseEvent::eReal);InitButtonEvent(contextMenuEvent,aEvent);contextMenuEvent.pressure=mLastMotionPressure;DispatchInputEvent(&contextMenuEvent);}}voidnsWindow::OnButtonReleaseEvent(GdkEventButton*aEvent){LOG(("Button %u release on %p\n",aEvent->button,(void*)this));uint16_tdomButton;switch(aEvent->button){case1:domButton=WidgetMouseEvent::eLeftButton;break;case2:domButton=WidgetMouseEvent::eMiddleButton;break;case3:domButton=WidgetMouseEvent::eRightButton;break;default:return;}gButtonState&=~ButtonMaskFromGDKButton(aEvent->button);WidgetMouseEventevent(true,eMouseUp,this,WidgetMouseEvent::eReal);event.button=domButton;InitButtonEvent(event,aEvent);gdoublepressure=0;gdk_event_get_axis((GdkEvent*)aEvent,GDK_AXIS_PRESSURE,&pressure);event.pressure=pressure?pressure:mLastMotionPressure;DispatchInputEvent(&event);mLastMotionPressure=pressure;}voidnsWindow::OnContainerFocusInEvent(GdkEventFocus*aEvent){LOGFOCUS(("OnContainerFocusInEvent [%p]\n",(void*)this));// Unset the urgency hint, if possibleGtkWidget*top_window=GetToplevelWidget();if(top_window&&(gtk_widget_get_visible(top_window)))SetUrgencyHint(top_window,false);// Return if being called within SetFocus because the focus manager// already knows that the window is active.if(gBlockActivateEvent){LOGFOCUS(("activated notification is blocked [%p]\n",(void*)this));return;}// If keyboard input will be accepted, the focus manager will call// SetFocus to set the correct window.gFocusWindow=nullptr;DispatchActivateEvent();if(!gFocusWindow){// We don't really have a window for dispatching key events, but// setting a non-nullptr value here prevents OnButtonPressEvent() from// dispatching an activation notification if the widget is already// active.gFocusWindow=this;}LOGFOCUS(("Events sent from focus in event [%p]\n",(void*)this));}voidnsWindow::OnContainerFocusOutEvent(GdkEventFocus*aEvent){LOGFOCUS(("OnContainerFocusOutEvent [%p]\n",(void*)this));if(mWindowType==eWindowType_toplevel||mWindowType==eWindowType_dialog){nsCOMPtr<nsIDragService>dragService=do_GetService(kCDragServiceCID);nsCOMPtr<nsIDragSession>dragSession;dragService->GetCurrentSession(getter_AddRefs(dragSession));// Rollup popups when a window is focused out unless a drag is occurring.// This check is because drags grab the keyboard and cause a focus out on// versions of GTK before 2.18.boolshouldRollup=!dragSession;if(!shouldRollup){// we also roll up when a drag is from a different applicationnsCOMPtr<nsIDOMNode>sourceNode;dragSession->GetSourceNode(getter_AddRefs(sourceNode));shouldRollup=(sourceNode==nullptr);}if(shouldRollup){CheckForRollup(0,0,false,true);}}if(gFocusWindow){RefPtr<nsWindow>kungFuDeathGrip=gFocusWindow;if(gFocusWindow->mIMContext){gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow);}gFocusWindow=nullptr;}DispatchDeactivateEvent();LOGFOCUS(("Done with container focus out [%p]\n",(void*)this));}boolnsWindow::DispatchCommandEvent(nsIAtom*aCommand){nsEventStatusstatus;WidgetCommandEventevent(true,nsGkAtoms::onAppCommand,aCommand,this);DispatchEvent(&event,status);returnTRUE;}boolnsWindow::DispatchContentCommandEvent(EventMessageaMsg){nsEventStatusstatus;WidgetContentCommandEventevent(true,aMsg,this);DispatchEvent(&event,status);returnTRUE;}staticboolIsCtrlAltTab(GdkEventKey*aEvent){returnaEvent->keyval==GDK_Tab&&KeymapWrapper::AreModifiersActive(KeymapWrapper::CTRL|KeymapWrapper::ALT,aEvent->state);}boolnsWindow::DispatchKeyDownEvent(GdkEventKey*aEvent,bool*aCancelled){NS_PRECONDITION(aCancelled,"aCancelled must not be null");*aCancelled=false;if(IsCtrlAltTab(aEvent)){returnfalse;}RefPtr<TextEventDispatcher>dispatcher=GetTextEventDispatcher();nsresultrv=dispatcher->BeginNativeInputTransaction();if(NS_WARN_IF(NS_FAILED(rv))){returnFALSE;}WidgetKeyboardEventkeydownEvent(true,eKeyDown,this);KeymapWrapper::InitKeyEvent(keydownEvent,aEvent);nsEventStatusstatus=nsEventStatus_eIgnore;booldispatched=dispatcher->DispatchKeyboardEvent(eKeyDown,keydownEvent,status,aEvent);*aCancelled=(status==nsEventStatus_eConsumeNoDefault);returndispatched?TRUE:FALSE;}WidgetEventTimensWindow::GetWidgetEventTime(guint32aEventTime){returnWidgetEventTime(aEventTime,GetEventTimeStamp(aEventTime));}TimeStampnsWindow::GetEventTimeStamp(guint32aEventTime){if(MOZ_UNLIKELY(!mGdkWindow)){// nsWindow has been Destroy()ed.returnTimeStamp::Now();}if(aEventTime==0){// Some X11 and GDK events may be received with a time of 0 to indicate// that they are synthetic events. Some input method editors do this.// In this case too, just return the current timestamp.returnTimeStamp::Now();}CurrentX11TimeGetter*getCurrentTime=GetCurrentTimeGetter();MOZ_ASSERT(getCurrentTime,"Null current time getter despite having a window");returnTimeConverter().GetTimeStampFromSystemTime(aEventTime,*getCurrentTime);}mozilla::CurrentX11TimeGetter*nsWindow::GetCurrentTimeGetter(){MOZ_ASSERT(mGdkWindow,"Expected mGdkWindow to be set");if(MOZ_UNLIKELY(!mCurrentTimeGetter)){mCurrentTimeGetter=MakeUnique<CurrentX11TimeGetter>(mGdkWindow);}returnmCurrentTimeGetter.get();}gbooleannsWindow::OnKeyPressEvent(GdkEventKey*aEvent){LOGFOCUS(("OnKeyPressEvent [%p]\n",(void*)this));// if we are in the middle of composing text, XIM gets to see it// before mozilla does.// FYI: Don't dispatch keydown event before notifying IME of the event// because IME may send a key event synchronously and consume the// original event.boolIMEWasEnabled=false;if(mIMContext){IMEWasEnabled=mIMContext->IsEnabled();if(mIMContext->OnKeyEvent(this,aEvent)){returnTRUE;}}// work around for annoying things.if(IsCtrlAltTab(aEvent)){returnTRUE;}nsCOMPtr<nsIWidget>kungFuDeathGrip=this;// Dispatch keydown event always. At auto repeating, we should send// KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP// However, old distributions (e.g., Ubuntu 9.10) sent native key// release event, so, on such platform, the DOM events will be:// KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...boolisKeyDownCancelled=false;if(DispatchKeyDownEvent(aEvent,&isKeyDownCancelled)&&(MOZ_UNLIKELY(mIsDestroyed)||isKeyDownCancelled)){returnTRUE;}// If a keydown event handler causes to enable IME, i.e., it moves// focus from IME unusable content to IME usable editor, we should// send the native key event to IME for the first input on the editor.if(!IMEWasEnabled&&mIMContext&&mIMContext->IsEnabled()){// Notice our keydown event was already dispatched. This prevents// unnecessary DOM keydown event in the editor.if(mIMContext->OnKeyEvent(this,aEvent,true)){returnTRUE;}}// Look for specialized app-command keysswitch(aEvent->keyval){caseGDK_Back:returnDispatchCommandEvent(nsGkAtoms::Back);caseGDK_Forward:returnDispatchCommandEvent(nsGkAtoms::Forward);caseGDK_Refresh:returnDispatchCommandEvent(nsGkAtoms::Reload);caseGDK_Stop:returnDispatchCommandEvent(nsGkAtoms::Stop);caseGDK_Search:returnDispatchCommandEvent(nsGkAtoms::Search);caseGDK_Favorites:returnDispatchCommandEvent(nsGkAtoms::Bookmarks);caseGDK_HomePage:returnDispatchCommandEvent(nsGkAtoms::Home);caseGDK_Copy:caseGDK_F16:// F16, F20, F18, F14 are old keysyms for Copy Cut Paste UndoreturnDispatchContentCommandEvent(eContentCommandCopy);caseGDK_Cut:caseGDK_F20:returnDispatchContentCommandEvent(eContentCommandCut);caseGDK_Paste:caseGDK_F18:returnDispatchContentCommandEvent(eContentCommandPaste);caseGDK_Redo:returnDispatchContentCommandEvent(eContentCommandRedo);caseGDK_Undo:caseGDK_F14:returnDispatchContentCommandEvent(eContentCommandUndo);}WidgetKeyboardEventkeypressEvent(true,eKeyPress,this);KeymapWrapper::InitKeyEvent(keypressEvent,aEvent);// before we dispatch a key, check if it's the context menu key.// If so, send a context menu key event instead.if(MaybeDispatchContextMenuEvent(aEvent)){returnTRUE;}RefPtr<TextEventDispatcher>dispatcher=GetTextEventDispatcher();nsresultrv=dispatcher->BeginNativeInputTransaction();if(NS_WARN_IF(NS_FAILED(rv))){returnTRUE;}// If the character code is in the BMP, send the key press event.// Otherwise, send a compositionchange event with the equivalent UTF-16// string.// TODO: Investigate other browser's behavior in this case because// this hack is odd for UI Events.nsEventStatusstatus=nsEventStatus_eIgnore;if(keypressEvent.mKeyNameIndex!=KEY_NAME_INDEX_USE_STRING||keypressEvent.mKeyValue.Length()==1){dispatcher->MaybeDispatchKeypressEvents(keypressEvent,status,aEvent);}else{WidgetEventTimeeventTime=GetWidgetEventTime(aEvent->time);dispatcher->CommitComposition(status,&keypressEvent.mKeyValue,&eventTime);}returnTRUE;}boolnsWindow::MaybeDispatchContextMenuEvent(constGdkEventKey*aEvent){KeyNameIndexkeyNameIndex=KeymapWrapper::ComputeDOMKeyNameIndex(aEvent);// Shift+F10 and ContextMenu should cause eContextMenu event.if(keyNameIndex!=KEY_NAME_INDEX_F10&&keyNameIndex!=KEY_NAME_INDEX_ContextMenu){returnfalse;}WidgetMouseEventcontextMenuEvent(true,eContextMenu,this,WidgetMouseEvent::eReal,WidgetMouseEvent::eContextMenuKey);contextMenuEvent.mRefPoint=LayoutDeviceIntPoint(0,0);contextMenuEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));contextMenuEvent.mClickCount=1;KeymapWrapper::InitInputEvent(contextMenuEvent,aEvent->state);if(contextMenuEvent.IsControl()||contextMenuEvent.IsMeta()||contextMenuEvent.IsAlt()){returnfalse;}// If the key is ContextMenu, then an eContextMenu mouse event is// dispatched regardless of the state of the Shift modifier. When it is// pressed without the Shift modifier, a web page can prevent the default// context menu action. When pressed with the Shift modifier, the web page// cannot prevent the default context menu action.// (PresShell::HandleEventInternal() sets mOnlyChromeDispatch to true.)// If the key is F10, it needs Shift state because Shift+F10 is well-known// shortcut key on Linux. However, eContextMenu with Shift state is// special. It won't fire "contextmenu" event in the web content for// blocking web page to prevent its default. Therefore, this combination// should work same as ContextMenu key.// XXX Should we allow to block web page to prevent its default with// Ctrl+Shift+F10 or Alt+Shift+F10 instead?if(keyNameIndex==KEY_NAME_INDEX_F10){if(!contextMenuEvent.IsShift()){returnfalse;}contextMenuEvent.mModifiers&=~MODIFIER_SHIFT;}DispatchInputEvent(&contextMenuEvent);returntrue;}gbooleannsWindow::OnKeyReleaseEvent(GdkEventKey*aEvent){LOGFOCUS(("OnKeyReleaseEvent [%p]\n",(void*)this));if(mIMContext&&mIMContext->OnKeyEvent(this,aEvent)){returnTRUE;}RefPtr<TextEventDispatcher>dispatcher=GetTextEventDispatcher();nsresultrv=dispatcher->BeginNativeInputTransaction();if(NS_WARN_IF(NS_FAILED(rv))){returnfalse;}WidgetKeyboardEventkeyupEvent(true,eKeyUp,this);KeymapWrapper::InitKeyEvent(keyupEvent,aEvent);nsEventStatusstatus=nsEventStatus_eIgnore;dispatcher->DispatchKeyboardEvent(eKeyUp,keyupEvent,status,aEvent);returnTRUE;}voidnsWindow::OnScrollEvent(GdkEventScroll*aEvent){// check to see if we should rollupif(CheckForRollup(aEvent->x_root,aEvent->y_root,true,false))return;#if GTK_CHECK_VERSION(3,4,0)// check for duplicate legacy scroll event, see GNOME bug 726878if(aEvent->direction!=GDK_SCROLL_SMOOTH&&mLastScrollEventTime==aEvent->time)return;#endifWidgetWheelEventwheelEvent(true,eWheel,this);wheelEvent.mDeltaMode=nsIDOMWheelEvent::DOM_DELTA_LINE;switch(aEvent->direction){#if GTK_CHECK_VERSION(3,4,0)caseGDK_SCROLL_SMOOTH:{// As of GTK 3.4, all directional scroll events are provided by// the GDK_SCROLL_SMOOTH direction on XInput2 devices.mLastScrollEventTime=aEvent->time;// TODO - use a more appropriate scrolling unit than lines.// Multiply event deltas by 3 to emulate legacy behaviour.wheelEvent.mDeltaX=aEvent->delta_x*3;wheelEvent.mDeltaY=aEvent->delta_y*3;wheelEvent.mIsNoLineOrPageDelta=true;// This next step manually unsets smooth scrolling for touch devices// that trigger GDK_SCROLL_SMOOTH. We use the slave device, which// represents the actual input.GdkDevice*device=gdk_event_get_source_device((GdkEvent*)aEvent);GdkInputSourcesource=gdk_device_get_source(device);if(source==GDK_SOURCE_TOUCHSCREEN||source==GDK_SOURCE_TOUCHPAD){wheelEvent.mScrollType=WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;}break;}#endifcaseGDK_SCROLL_UP:wheelEvent.mDeltaY=wheelEvent.mLineOrPageDeltaY=-3;break;caseGDK_SCROLL_DOWN:wheelEvent.mDeltaY=wheelEvent.mLineOrPageDeltaY=3;break;caseGDK_SCROLL_LEFT:wheelEvent.mDeltaX=wheelEvent.mLineOrPageDeltaX=-1;break;caseGDK_SCROLL_RIGHT:wheelEvent.mDeltaX=wheelEvent.mLineOrPageDeltaX=1;break;}wheelEvent.mRefPoint=GetRefPoint(this,aEvent);KeymapWrapper::InitInputEvent(wheelEvent,aEvent->state);wheelEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));DispatchInputEvent(&wheelEvent);}voidnsWindow::OnVisibilityNotifyEvent(GdkEventVisibility*aEvent){LOGDRAW(("Visibility event %i on [%p] %p\n",aEvent->state,this,aEvent->window));if(!mGdkWindow)return;switch(aEvent->state){caseGDK_VISIBILITY_UNOBSCURED:caseGDK_VISIBILITY_PARTIAL:if(mIsFullyObscured&&mHasMappedToplevel){// GDK_EXPOSE events have been ignored, so make sure GDK// doesn't think that the window has already been painted.gdk_window_invalidate_rect(mGdkWindow,nullptr,FALSE);}mIsFullyObscured=false;// if we have to retry the grab, retry it.EnsureGrabs();break;default:// includes GDK_VISIBILITY_FULLY_OBSCUREDmIsFullyObscured=true;break;}}voidnsWindow::OnWindowStateEvent(GtkWidget*aWidget,GdkEventWindowState*aEvent){LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",(void*)this,aEvent->changed_mask,aEvent->new_window_state));if(IS_MOZ_CONTAINER(aWidget)){// This event is notifying the container widget of changes to the// toplevel window. Just detect changes affecting whether windows are// viewable.//// (A visibility notify event is sent to each window that becomes// viewable when the toplevel is mapped, but we can't rely on that for// setting mHasMappedToplevel because these toplevel window state// events are asynchronous. The windows in the hierarchy now may not// be the same windows as when the toplevel was mapped, so they may// not get VisibilityNotify events.)boolmapped=!(aEvent->new_window_state&(GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));if(mHasMappedToplevel!=mapped){SetHasMappedToplevel(mapped);}return;}// else the widget is a shell widget.// We don't care about anything but changes in the maximized/icon/fullscreen// statesif((aEvent->changed_mask&(GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))==0){return;}if(aEvent->new_window_state&GDK_WINDOW_STATE_ICONIFIED){LOG(("\tIconified\n"));mSizeState=nsSizeMode_Minimized;#ifdef ACCESSIBILITYDispatchMinimizeEventAccessible();#endif //ACCESSIBILITY}elseif(aEvent->new_window_state&GDK_WINDOW_STATE_FULLSCREEN){LOG(("\tFullscreen\n"));mSizeState=nsSizeMode_Fullscreen;}elseif(aEvent->new_window_state&GDK_WINDOW_STATE_MAXIMIZED){LOG(("\tMaximized\n"));mSizeState=nsSizeMode_Maximized;#ifdef ACCESSIBILITYDispatchMaximizeEventAccessible();#endif //ACCESSIBILITY}else{LOG(("\tNormal\n"));mSizeState=nsSizeMode_Normal;#ifdef ACCESSIBILITYDispatchRestoreEventAccessible();#endif //ACCESSIBILITY}if(mWidgetListener){mWidgetListener->SizeModeChanged(mSizeState);if(aEvent->changed_mask&GDK_WINDOW_STATE_FULLSCREEN){mWidgetListener->FullscreenChanged(aEvent->new_window_state&GDK_WINDOW_STATE_FULLSCREEN);}}}voidnsWindow::ThemeChanged(){NotifyThemeChanged();if(!mGdkWindow||MOZ_UNLIKELY(mIsDestroyed))return;// Dispatch theme change notification to all child windowsGList*children=gdk_window_peek_children(mGdkWindow);while(children){GdkWindow*gdkWin=GDK_WINDOW(children->data);auto*win=(nsWindow*)g_object_get_data(G_OBJECT(gdkWin),"nsWindow");if(win&&win!=this){// guard against infinite recursionRefPtr<nsWindow>kungFuDeathGrip=win;win->ThemeChanged();}children=children->next;}}voidnsWindow::OnDPIChanged(){if(mWidgetListener){nsIPresShell*presShell=mWidgetListener->GetPresShell();if(presShell){presShell->BackingScaleFactorChanged();// Update menu's font size etcpresShell->ThemeChanged();}}}voidnsWindow::OnCheckResize(){mPendingConfigures++;}voidnsWindow::OnCompositedChanged(){if(mWidgetListener){nsIPresShell*presShell=mWidgetListener->GetPresShell();if(presShell){// Update CSD after the change in alpha visibilitypresShell->ThemeChanged();}}CleanLayerManagerRecursive();}voidnsWindow::DispatchDragEvent(EventMessageaMsg,constLayoutDeviceIntPoint&aRefPoint,guintaTime){WidgetDragEventevent(true,aMsg,this);InitDragEvent(event);event.mRefPoint=aRefPoint;event.AssignEventTime(GetWidgetEventTime(aTime));DispatchInputEvent(&event);}voidnsWindow::OnDragDataReceivedEvent(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,GtkSelectionData*aSelectionData,guintaInfo,guintaTime,gpointeraData){LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n",(void*)this));RefPtr<nsDragService>dragService=nsDragService::GetInstance();dragService->TargetDataReceived(aWidget,aDragContext,aX,aY,aSelectionData,aInfo,aTime);}#if GTK_CHECK_VERSION(3,4,0)gbooleannsWindow::OnTouchEvent(GdkEventTouch*aEvent){if(!mHandleTouchEvent){returnFALSE;}EventMessagemsg;switch(aEvent->type){caseGDK_TOUCH_BEGIN:msg=eTouchStart;break;caseGDK_TOUCH_UPDATE:msg=eTouchMove;break;caseGDK_TOUCH_END:msg=eTouchEnd;break;caseGDK_TOUCH_CANCEL:msg=eTouchCancel;break;default:returnFALSE;}LayoutDeviceIntPointtouchPoint=GetRefPoint(this,aEvent);int32_tid;RefPtr<dom::Touch>touch;if(mTouches.Remove(aEvent->sequence,getter_AddRefs(touch))){id=touch->mIdentifier;}else{id=++gLastTouchID&0x7FFFFFFF;}touch=newdom::Touch(id,touchPoint,LayoutDeviceIntPoint(1,1),0.0f,0.0f);WidgetTouchEventevent(true,msg,this);KeymapWrapper::InitInputEvent(event,aEvent->state);event.mTime=aEvent->time;if(aEvent->type==GDK_TOUCH_BEGIN||aEvent->type==GDK_TOUCH_UPDATE){mTouches.Put(aEvent->sequence,touch.forget());// add all touch points to event objectfor(autoiter=mTouches.Iter();!iter.Done();iter.Next()){event.mTouches.AppendElement(newdom::Touch(*iter.UserData()));}}elseif(aEvent->type==GDK_TOUCH_END||aEvent->type==GDK_TOUCH_CANCEL){*event.mTouches.AppendElement()=touch.forget();}DispatchInputEvent(&event);returnTRUE;}#endifstaticvoidGetBrandName(nsXPIDLString&brandName){nsCOMPtr<nsIStringBundleService>bundleService=do_GetService(NS_STRINGBUNDLE_CONTRACTID);nsCOMPtr<nsIStringBundle>bundle;if(bundleService)bundleService->CreateBundle("chrome://branding/locale/brand.properties",getter_AddRefs(bundle));if(bundle)bundle->GetStringFromName(u"brandShortName",getter_Copies(brandName));if(brandName.IsEmpty())brandName.AssignLiteral(u"Mozilla");}staticGdkWindow*CreateGdkWindow(GdkWindow*parent,GtkWidget*widget){GdkWindowAttrattributes;gintattributes_mask=GDK_WA_VISUAL;attributes.event_mask=kEvents;attributes.width=1;attributes.height=1;attributes.wclass=GDK_INPUT_OUTPUT;attributes.visual=gtk_widget_get_visual(widget);attributes.window_type=GDK_WINDOW_CHILD;#if (MOZ_WIDGET_GTK == 2)attributes_mask|=GDK_WA_COLORMAP;attributes.colormap=gtk_widget_get_colormap(widget);#endifGdkWindow*window=gdk_window_new(parent,&attributes,attributes_mask);gdk_window_set_user_data(window,widget);// GTK3 TODO?#if (MOZ_WIDGET_GTK == 2)/* set the default pixmap to None so that you don't end up with the gtk default which is BlackPixel. */gdk_window_set_back_pixmap(window,nullptr,FALSE);#endifreturnwindow;}nsresultnsWindow::Create(nsIWidget*aParent,nsNativeWidgetaNativeParent,constLayoutDeviceIntRect&aRect,nsWidgetInitData*aInitData){// only set the base parent if we're going to be a dialog or a// toplevelnsIWidget*baseParent=aInitData&&(aInitData->mWindowType==eWindowType_dialog||aInitData->mWindowType==eWindowType_toplevel||aInitData->mWindowType==eWindowType_invisible)?nullptr:aParent;#ifdef ACCESSIBILITY// Send a DBus message to check whether a11y is enableda11y::PreInit();#endif// Ensure that the toolkit is created.nsGTKToolkit::GetToolkit();// initialize all the common bits of this classBaseCreate(baseParent,aInitData);// Do we need to listen for resizes?boollistenForResizes=false;;if(aNativeParent||(aInitData&&aInitData->mListenForResizes))listenForResizes=true;// and do our common creationCommonCreate(aParent,listenForResizes);// save our boundsmBounds=aRect;ConstrainSize(&mBounds.width,&mBounds.height);// figure out our parent windowGtkWidget*parentMozContainer=nullptr;GtkContainer*parentGtkContainer=nullptr;GdkWindow*parentGdkWindow=nullptr;GtkWindow*topLevelParent=nullptr;nsWindow*parentnsWindow=nullptr;GtkWidget*eventWidget=nullptr;boolshellHasCSD=false;if(aParent){parentnsWindow=static_cast<nsWindow*>(aParent);parentGdkWindow=parentnsWindow->mGdkWindow;}elseif(aNativeParent&&GDK_IS_WINDOW(aNativeParent)){parentGdkWindow=GDK_WINDOW(aNativeParent);parentnsWindow=get_window_for_gdk_window(parentGdkWindow);if(!parentnsWindow)returnNS_ERROR_FAILURE;}elseif(aNativeParent&>K_IS_CONTAINER(aNativeParent)){parentGtkContainer=GTK_CONTAINER(aNativeParent);}if(parentGdkWindow){// get the widget for the window - it should be a moz containerparentMozContainer=parentnsWindow->GetMozContainerWidget();if(!parentMozContainer)returnNS_ERROR_FAILURE;// get the toplevel window just in case someone needs to use it// for setting transients or whatever.topLevelParent=GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));}// ok, create our windowsswitch(mWindowType){caseeWindowType_dialog:caseeWindowType_popup:caseeWindowType_toplevel:caseeWindowType_invisible:{mIsTopLevel=true;// Popups that are not noautohide are only temporary. The are used// for menus and the like and disappear when another window is used.// For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,// which will use a Window with the override-redirect attribute// (for temporary windows).// For long-lived windows, their stacking order is managed by the// window manager, as indicated by GTK_WINDOW_TOPLEVEL ...GtkWindowTypetype=mWindowType!=eWindowType_popup||aInitData->mNoAutoHide?GTK_WINDOW_TOPLEVEL:GTK_WINDOW_POPUP;mShell=gtk_window_new(type);booluseAlphaVisual=(mWindowType==eWindowType_popup&&aInitData->mSupportTranslucency);// mozilla.widget.use-argb-visuals is a hidden pref defaulting to false// to allow experimentationif(Preferences::GetBool("mozilla.widget.use-argb-visuals",false))useAlphaVisual=true;// We need to select an ARGB visual here instead of in// SetTransparencyMode() because it has to be done before the// widget is realized. An ARGB visual is only useful if we// are on a compositing window manager.if(useAlphaVisual){GdkScreen*screen=gtk_widget_get_screen(mShell);if(gdk_screen_is_composited(screen)){#if (MOZ_WIDGET_GTK == 2)GdkColormap*colormap=gdk_screen_get_rgba_colormap(screen);gtk_widget_set_colormap(mShell,colormap);#elseGdkVisual*visual=gdk_screen_get_rgba_visual(screen);gtk_widget_set_visual(mShell,visual);#endif}}// We only move a general managed toplevel window if someone has// actually placed the window somewhere. If no placement has taken// place, we just let the window manager Do The Right Thing.NativeResize();if(mWindowType==eWindowType_dialog){SetDefaultIcon();gtk_window_set_wmclass(GTK_WINDOW(mShell),"Dialog",gdk_get_program_class());gtk_window_set_type_hint(GTK_WINDOW(mShell),GDK_WINDOW_TYPE_HINT_DIALOG);gtk_window_set_transient_for(GTK_WINDOW(mShell),topLevelParent);}elseif(mWindowType==eWindowType_popup){// With popup windows, we want to control their position, so don't// wait for the window manager to place them (which wouldn't// happen with override-redirect windows anyway).NativeMove();gtk_window_set_wmclass(GTK_WINDOW(mShell),"Popup",gdk_get_program_class());if(aInitData->mNoAutoHide){// ... but the window manager does not decorate this window,// nor provide a separate taskbar icon.if(mBorderStyle==eBorderStyle_default){gtk_window_set_decorated(GTK_WINDOW(mShell),FALSE);}else{booldecorate=mBorderStyle&eBorderStyle_title;gtk_window_set_decorated(GTK_WINDOW(mShell),decorate);if(decorate){gtk_window_set_deletable(GTK_WINDOW(mShell),mBorderStyle&eBorderStyle_close);}}gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell),TRUE);// Element focus is managed by the parent window so the// WM_HINTS input field is set to False to tell the window// manager not to set input focus to this window ...gtk_window_set_accept_focus(GTK_WINDOW(mShell),FALSE);#ifdef MOZ_X11// ... but when the window manager offers focus through// WM_TAKE_FOCUS, focus is requested on the parent window.gtk_widget_realize(mShell);gdk_window_add_filter(gtk_widget_get_window(mShell),popup_take_focus_filter,nullptr);#endif}GdkWindowTypeHintgtkTypeHint;if(aInitData->mIsDragPopup){gtkTypeHint=GDK_WINDOW_TYPE_HINT_DND;mIsDragPopup=true;}else{switch(aInitData->mPopupHint){caseePopupTypeMenu:gtkTypeHint=GDK_WINDOW_TYPE_HINT_POPUP_MENU;break;caseePopupTypeTooltip:gtkTypeHint=GDK_WINDOW_TYPE_HINT_TOOLTIP;break;default:gtkTypeHint=GDK_WINDOW_TYPE_HINT_UTILITY;break;}}gtk_window_set_type_hint(GTK_WINDOW(mShell),gtkTypeHint);if(topLevelParent){gtk_window_set_transient_for(GTK_WINDOW(mShell),topLevelParent);}}else{// must be eWindowType_toplevelSetDefaultIcon();gtk_window_set_wmclass(GTK_WINDOW(mShell),"Toplevel",gdk_get_program_class());// each toplevel window gets its own window groupGtkWindowGroup*group=gtk_window_group_new();gtk_window_group_add_window(group,GTK_WINDOW(mShell));g_object_unref(group);}// Create a container to hold child windows and child GtkWidgets.GtkWidget*container=moz_container_new();mContainer=MOZ_CONTAINER(container);#if (MOZ_WIDGET_GTK == 3)// "csd" style is set when widget is realized so we need to call// it explicitly now.gtk_widget_realize(mShell);// We can't draw directly to top-level window when client side// decorations are enabled. We use container with GdkWindow instead.GtkStyleContext*style=gtk_widget_get_style_context(mShell);shellHasCSD=gtk_style_context_has_class(style,"csd");#endifif(!shellHasCSD){// Use mShell's window for drawing and events.gtk_widget_set_has_window(container,FALSE);// Prevent GtkWindow from painting a background to flicker.gtk_widget_set_app_paintable(mShell,TRUE);}// Set up event widgeteventWidget=shellHasCSD?container:mShell;gtk_widget_add_events(eventWidget,kEvents);gtk_container_add(GTK_CONTAINER(mShell),container);gtk_widget_realize(container);// make sure this is the focus widget in the containergtk_widget_show(container);gtk_widget_grab_focus(container);// the drawing windowmGdkWindow=gtk_widget_get_window(eventWidget);if(mWindowType==eWindowType_popup){// gdk does not automatically set the cursor for "temporary"// windows, which are what gtk uses for popups.mCursor=eCursor_wait;// force SetCursor to actually set the// cursor, even though our internal state// indicates that we already have the// standard cursor.SetCursor(eCursor_standard);if(aInitData->mNoAutoHide){gintwmd=ConvertBorderStyles(mBorderStyle);if(wmd!=-1)gdk_window_set_decorations(mGdkWindow,(GdkWMDecoration)wmd);}// If the popup ignores mouse events, set an empty input shape.if(aInitData->mMouseTransparent){#if (MOZ_WIDGET_GTK == 2)GdkRectanglerect={0,0,0,0};GdkRegion*region=gdk_region_rectangle(&rect);gdk_window_input_shape_combine_region(mGdkWindow,region,0,0);gdk_region_destroy(region);#elsecairo_rectangle_int_trect={0,0,0,0};cairo_region_t*region=cairo_region_create_rectangle(&rect);gdk_window_input_shape_combine_region(mGdkWindow,region,0,0);cairo_region_destroy(region);#endif}}}break;caseeWindowType_plugin:caseeWindowType_plugin_ipc_chrome:caseeWindowType_plugin_ipc_content:MOZ_ASSERT_UNREACHABLE();returnNS_ERROR_FAILURE;caseeWindowType_child:{if(parentMozContainer){mGdkWindow=CreateGdkWindow(parentGdkWindow,parentMozContainer);mHasMappedToplevel=parentnsWindow->mHasMappedToplevel;}elseif(parentGtkContainer){// This MozContainer has its own window for drawing and receives// events because there is no mShell widget (corresponding to this// nsWindow).GtkWidget*container=moz_container_new();mContainer=MOZ_CONTAINER(container);eventWidget=container;gtk_widget_add_events(eventWidget,kEvents);gtk_container_add(parentGtkContainer,container);gtk_widget_realize(container);mGdkWindow=gtk_widget_get_window(container);}else{NS_WARNING("Warning: tried to create a new child widget with no parent!");returnNS_ERROR_FAILURE;}}break;default:break;}// label the drawing window with this object so we can find our way homeg_object_set_data(G_OBJECT(mGdkWindow),"nsWindow",this);if(mContainer)g_object_set_data(G_OBJECT(mContainer),"nsWindow",this);if(mShell)g_object_set_data(G_OBJECT(mShell),"nsWindow",this);// attach listeners for eventsif(mShell){g_signal_connect(mShell,"configure_event",G_CALLBACK(configure_event_cb),nullptr);g_signal_connect(mShell,"delete_event",G_CALLBACK(delete_event_cb),nullptr);g_signal_connect(mShell,"window_state_event",G_CALLBACK(window_state_event_cb),nullptr);g_signal_connect(mShell,"check-resize",G_CALLBACK(check_resize_cb),nullptr);g_signal_connect(mShell,"composited-changed",G_CALLBACK(composited_changed_cb),nullptr);GtkSettings*default_settings=gtk_settings_get_default();g_signal_connect_after(default_settings,"notify::gtk-theme-name",G_CALLBACK(theme_changed_cb),this);g_signal_connect_after(default_settings,"notify::gtk-font-name",G_CALLBACK(theme_changed_cb),this);}if(mContainer){// Widget signalsg_signal_connect(mContainer,"unrealize",G_CALLBACK(container_unrealize_cb),nullptr);g_signal_connect_after(mContainer,"size_allocate",G_CALLBACK(size_allocate_cb),nullptr);g_signal_connect(mContainer,"hierarchy-changed",G_CALLBACK(hierarchy_changed_cb),nullptr);#if (MOZ_WIDGET_GTK == 3)g_signal_connect(mContainer,"notify::scale-factor",G_CALLBACK(scale_changed_cb),nullptr);#endif// Initialize mHasMappedToplevel.hierarchy_changed_cb(GTK_WIDGET(mContainer),nullptr);// Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW// widgets.#if (MOZ_WIDGET_GTK == 2)g_signal_connect(mContainer,"expose_event",G_CALLBACK(expose_event_cb),nullptr);#elseg_signal_connect(G_OBJECT(mContainer),"draw",G_CALLBACK(expose_event_cb),nullptr);#endifg_signal_connect(mContainer,"focus_in_event",G_CALLBACK(focus_in_event_cb),nullptr);g_signal_connect(mContainer,"focus_out_event",G_CALLBACK(focus_out_event_cb),nullptr);g_signal_connect(mContainer,"key_press_event",G_CALLBACK(key_press_event_cb),nullptr);g_signal_connect(mContainer,"key_release_event",G_CALLBACK(key_release_event_cb),nullptr);gtk_drag_dest_set((GtkWidget*)mContainer,(GtkDestDefaults)0,nullptr,0,(GdkDragAction)0);g_signal_connect(mContainer,"drag_motion",G_CALLBACK(drag_motion_event_cb),nullptr);g_signal_connect(mContainer,"drag_leave",G_CALLBACK(drag_leave_event_cb),nullptr);g_signal_connect(mContainer,"drag_drop",G_CALLBACK(drag_drop_event_cb),nullptr);g_signal_connect(mContainer,"drag_data_received",G_CALLBACK(drag_data_received_event_cb),nullptr);GtkWidget*widgets[]={GTK_WIDGET(mContainer),!shellHasCSD?mShell:nullptr};for(size_ti=0;i<ArrayLength(widgets)&&widgets[i];++i){// Visibility events are sent to the owning widget of the relevant// window but do not propagate to parent widgets so connect on// mShell (if it exists) as well as mContainer.g_signal_connect(widgets[i],"visibility-notify-event",G_CALLBACK(visibility_notify_event_cb),nullptr);// Similarly double buffering is controlled by the window's owning// widget. Disable double buffering for painting directly to the// X Window.gtk_widget_set_double_buffered(widgets[i],FALSE);}// We create input contexts for all containers, except for// toplevel popup windowsif(mWindowType!=eWindowType_popup){mIMContext=newIMContextWrapper(this);}}elseif(!mIMContext){nsWindow*container=GetContainerWindow();if(container){mIMContext=container->mIMContext;}}if(eventWidget){#if (MOZ_WIDGET_GTK == 2)// Don't let GTK mess with the shapes of our GdkWindowsGTK_PRIVATE_SET_FLAG(eventWidget,GTK_HAS_SHAPE_MASK);#endif// These events are sent to the owning widget of the relevant window// and propagate up to the first widget that handles the events, so we// need only connect on mShell, if it exists, to catch events on its// window and windows of mContainer.g_signal_connect(eventWidget,"enter-notify-event",G_CALLBACK(enter_notify_event_cb),nullptr);g_signal_connect(eventWidget,"leave-notify-event",G_CALLBACK(leave_notify_event_cb),nullptr);g_signal_connect(eventWidget,"motion-notify-event",G_CALLBACK(motion_notify_event_cb),nullptr);g_signal_connect(eventWidget,"button-press-event",G_CALLBACK(button_press_event_cb),nullptr);g_signal_connect(eventWidget,"button-release-event",G_CALLBACK(button_release_event_cb),nullptr);g_signal_connect(eventWidget,"property-notify-event",G_CALLBACK(property_notify_event_cb),nullptr);g_signal_connect(eventWidget,"scroll-event",G_CALLBACK(scroll_event_cb),nullptr);#if GTK_CHECK_VERSION(3,4,0)g_signal_connect(eventWidget,"touch-event",G_CALLBACK(touch_event_cb),nullptr);#endif}LOG(("nsWindow [%p]\n",(void*)this));if(mShell){LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",mShell,mContainer,mGdkWindow,gdk_x11_window_get_xid(mGdkWindow)));}elseif(mContainer){LOG(("\tmContainer %p mGdkWindow %p\n",mContainer,mGdkWindow));}elseif(mGdkWindow){LOG(("\tmGdkWindow %p parent %p\n",mGdkWindow,gdk_window_get_parent(mGdkWindow)));}// resize so that everything is set to the right dimensionsif(!mIsTopLevel)Resize(mBounds.x,mBounds.y,mBounds.width,mBounds.height,false);#ifdef MOZ_X11if(mIsX11Display&&mGdkWindow){mXDisplay=GDK_WINDOW_XDISPLAY(mGdkWindow);mXWindow=gdk_x11_window_get_xid(mGdkWindow);GdkVisual*gdkVisual=gdk_window_get_visual(mGdkWindow);mXVisual=gdk_x11_visual_get_xvisual(gdkVisual);mXDepth=gdk_visual_get_depth(gdkVisual);mSurfaceProvider.Initialize(mXDisplay,mXWindow,mXVisual,mXDepth);}#endifreturnNS_OK;}voidnsWindow::SetWindowClass(constnsAString&xulWinType){if(!mShell)return;constchar*res_class=gdk_get_program_class();if(!res_class)return;char*res_name=ToNewCString(xulWinType);if(!res_name)return;constchar*role=nullptr;// Parse res_name into a name and role. Characters other than// [A-Za-z0-9_-] are converted to '_'. Anything after the first// colon is assigned to role; if there's no colon, assign the// whole thing to both role and res_name.for(char*c=res_name;*c;c++){if(':'==*c){*c=0;role=c+1;}elseif(!isascii(*c)||(!isalnum(*c)&&('_'!=*c)&&('-'!=*c)))*c='_';}res_name[0]=toupper(res_name[0]);if(!role)role=res_name;gdk_window_set_role(mGdkWindow,role);#ifdef MOZ_X11if(mIsX11Display){XClassHint*class_hint=XAllocClassHint();if(!class_hint){free(res_name);return;}class_hint->res_name=res_name;class_hint->res_class=const_cast<char*>(res_class);// Can't use gtk_window_set_wmclass() for this; it prints// a warning & refuses to make the change.GdkDisplay*display=gdk_display_get_default();XSetClassHint(GDK_DISPLAY_XDISPLAY(display),gdk_x11_window_get_xid(mGdkWindow),class_hint);XFree(class_hint);}#endif /* MOZ_X11 */free(res_name);}voidnsWindow::NativeResize(){if(!AreBoundsSane()){// If someone has set this so that the needs show flag is false// and it needs to be hidden, update the flag and hide the// window. This flag will be cleared the next time someone// hides the window or shows it. It also prevents us from// calling NativeShow(false) excessively on the window which// causes unneeded X traffic.if(!mNeedsShow&&mIsShown){mNeedsShow=true;NativeShow(false);}return;}GdkRectanglesize=DevicePixelsToGdkSizeRoundUp(mBounds.Size());LOG(("nsWindow::NativeResize [%p] %d %d\n",(void*)this,size.width,size.height));if(mIsTopLevel){gtk_window_resize(GTK_WINDOW(mShell),size.width,size.height);}elseif(mContainer){GtkWidget*widget=GTK_WIDGET(mContainer);GtkAllocationallocation,prev_allocation;gtk_widget_get_allocation(widget,&prev_allocation);allocation.x=prev_allocation.x;allocation.y=prev_allocation.y;allocation.width=size.width;allocation.height=size.height;gtk_widget_size_allocate(widget,&allocation);}elseif(mGdkWindow){gdk_window_resize(mGdkWindow,size.width,size.height);}#ifdef MOZ_X11// Notify the X11CompositorWidget of a ClientSizeChange// This is different than OnSizeAllocate to catch initial sizingif(mCompositorWidgetDelegate){mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());}#endif// Does it need to be shown because bounds were previously insane?if(mNeedsShow&&mIsShown){NativeShow(true);}}voidnsWindow::NativeMoveResize(){if(!AreBoundsSane()){// If someone has set this so that the needs show flag is false// and it needs to be hidden, update the flag and hide the// window. This flag will be cleared the next time someone// hides the window or shows it. It also prevents us from// calling NativeShow(false) excessively on the window which// causes unneeded X traffic.if(!mNeedsShow&&mIsShown){mNeedsShow=true;NativeShow(false);}NativeMove();}GdkRectanglesize=DevicePixelsToGdkSizeRoundUp(mBounds.Size());GdkPointtopLeft=DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());LOG(("nsWindow::NativeMoveResize [%p] %d %d %d %d\n",(void*)this,topLeft.x,topLeft.y,size.width,size.height));if(mIsTopLevel){// x and y give the position of the window manager frame top-left.gtk_window_move(GTK_WINDOW(mShell),topLeft.x,topLeft.y);// This sets the client window size.gtk_window_resize(GTK_WINDOW(mShell),size.width,size.height);}elseif(mContainer){GtkAllocationallocation;allocation.x=topLeft.x;allocation.y=topLeft.y;allocation.width=size.width;allocation.height=size.height;gtk_widget_size_allocate(GTK_WIDGET(mContainer),&allocation);}elseif(mGdkWindow){gdk_window_move_resize(mGdkWindow,topLeft.x,topLeft.y,size.width,size.height);}#ifdef MOZ_X11// Notify the X11CompositorWidget of a ClientSizeChange// This is different than OnSizeAllocate to catch initial sizingif(mCompositorWidgetDelegate){mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());}#endif// Does it need to be shown because bounds were previously insane?if(mNeedsShow&&mIsShown){NativeShow(true);}}voidnsWindow::NativeShow(boolaAction){if(aAction){// unset our flag now that our window has been shownmNeedsShow=false;if(mIsTopLevel){// Set up usertime/startupID metadata for the created window.if(mWindowType!=eWindowType_invisible){SetUserTimeAndStartupIDForActivatedWindow(mShell);}gtk_widget_show(mShell);}elseif(mContainer){gtk_widget_show(GTK_WIDGET(mContainer));}elseif(mGdkWindow){gdk_window_show_unraised(mGdkWindow);}}else{if(mIsTopLevel){// Workaround window freezes on GTK versions before 3.21.2 by// ensuring that configure events get dispatched to windows before// they are unmapped. See bug 1225044.if(gtk_check_version(3,21,2)!=nullptr&&mPendingConfigures>0){GtkAllocationallocation;gtk_widget_get_allocation(GTK_WIDGET(mShell),&allocation);GdkEventConfigureevent;PodZero(&event);event.type=GDK_CONFIGURE;event.window=mGdkWindow;event.send_event=TRUE;event.x=allocation.x;event.y=allocation.y;event.width=allocation.width;event.height=allocation.height;autoshellClass=GTK_WIDGET_GET_CLASS(mShell);for(unsignedinti=0;i<mPendingConfigures;i++){Unused<<shellClass->configure_event(mShell,&event);}mPendingConfigures=0;}gtk_widget_hide(mShell);ClearTransparencyBitmap();// Release some resources}elseif(mContainer){gtk_widget_hide(GTK_WIDGET(mContainer));}elseif(mGdkWindow){gdk_window_hide(mGdkWindow);}}}voidnsWindow::SetHasMappedToplevel(boolaState){// Even when aState == mHasMappedToplevel (as when this method is called// from Show()), child windows need to have their state checked, so don't// return early.boololdState=mHasMappedToplevel;mHasMappedToplevel=aState;// mHasMappedToplevel is not updated for children of windows that are// hidden; GDK knows not to send expose events for these windows. The// state is recorded on the hidden window itself, but, for child trees of// hidden windows, their state essentially becomes disconnected from their// hidden parent. When the hidden parent gets shown, the child trees are// reconnected, and the state of the window being shown can be easily// propagated.if(!mIsShown||!mGdkWindow)return;if(aState&&!oldState&&!mIsFullyObscured){// GDK_EXPOSE events have been ignored but the window is now visible,// so make sure GDK doesn't think that the window has already been// painted.gdk_window_invalidate_rect(mGdkWindow,nullptr,FALSE);// Check that a grab didn't fail due to the window not being// viewable.EnsureGrabs();}for(GList*children=gdk_window_peek_children(mGdkWindow);children;children=children->next){GdkWindow*gdkWin=GDK_WINDOW(children->data);nsWindow*child=get_window_for_gdk_window(gdkWin);if(child&&child->mHasMappedToplevel!=aState){child->SetHasMappedToplevel(aState);}}}LayoutDeviceIntSizensWindow::GetSafeWindowSize(LayoutDeviceIntSizeaSize){// The X protocol uses CARD32 for window sizes, but the server (1.11.3)// reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned)// CARD16 in the protocol, but the server's ProcCreatePixmap returns// BadAlloc if dimensions cannot be represented by signed shorts.LayoutDeviceIntSizeresult=aSize;constint32_tkInt16Max=32767;if(result.width>kInt16Max){result.width=kInt16Max;}if(result.height>kInt16Max){result.height=kInt16Max;}returnresult;}voidnsWindow::EnsureGrabs(void){if(mRetryPointerGrab)GrabPointer(sRetryGrabTime);}voidnsWindow::CleanLayerManagerRecursive(void){if(mLayerManager){mLayerManager->Destroy();mLayerManager=nullptr;}DestroyCompositor();GList*children=gdk_window_peek_children(mGdkWindow);for(GList*list=children;list;list=list->next){nsWindow*window=get_window_for_gdk_window(GDK_WINDOW(list->data));if(window){window->CleanLayerManagerRecursive();}}}voidnsWindow::SetTransparencyMode(nsTransparencyModeaMode){if(!mShell){// Pass the request to the toplevel windowGtkWidget*topWidget=GetToplevelWidget();if(!topWidget)return;nsWindow*topWindow=get_window_for_gtk_widget(topWidget);if(!topWindow)return;topWindow->SetTransparencyMode(aMode);return;}boolisTransparent=aMode==eTransparencyTransparent;if(mIsTransparent==isTransparent){return;}elseif(mWindowType!=eWindowType_popup){NS_WARNING("Cannot set transparency mode on non-popup windows.");return;}if(!isTransparent){ClearTransparencyBitmap();}// else the new default alpha values are "all 1", so we don't// need to change anything yetmIsTransparent=isTransparent;// Need to clean our LayerManager up while still alive because// we don't want to use layers acceleration on shaped windowsCleanLayerManagerRecursive();}nsTransparencyModensWindow::GetTransparencyMode(){if(!mShell){// Pass the request to the toplevel windowGtkWidget*topWidget=GetToplevelWidget();if(!topWidget){returneTransparencyOpaque;}nsWindow*topWindow=get_window_for_gtk_widget(topWidget);if(!topWindow){returneTransparencyOpaque;}returntopWindow->GetTransparencyMode();}returnmIsTransparent?eTransparencyTransparent:eTransparencyOpaque;}#if (MOZ_WIDGET_GTK >= 3)voidnsWindow::UpdateOpaqueRegion(constLayoutDeviceIntRegion&aOpaqueRegion){// Available as of GTK 3.10+staticautosGdkWindowSetOpaqueRegion=(void(*)(GdkWindow*,cairo_region_t*))dlsym(RTLD_DEFAULT,"gdk_window_set_opaque_region");if(sGdkWindowSetOpaqueRegion&&mGdkWindow&&gdk_window_get_window_type(mGdkWindow)==GDK_WINDOW_TOPLEVEL){if(aOpaqueRegion.IsEmpty()){(*sGdkWindowSetOpaqueRegion)(mGdkWindow,nullptr);}else{cairo_region_t*region=cairo_region_create();for(autoiter=aOpaqueRegion.RectIter();!iter.Done();iter.Next()){constLayoutDeviceIntRect&r=iter.Get();cairo_rectangle_int_trect={r.x,r.y,r.width,r.height};cairo_region_union_rectangle(region,&rect);}(*sGdkWindowSetOpaqueRegion)(mGdkWindow,region);cairo_region_destroy(region);}}}#endifnsresultnsWindow::ConfigureChildren(constnsTArray<Configuration>&aConfigurations){// If this is a remotely updated widget we receive clipping, position, and// size information from a source other than our owner. Don't let our parent// update this information.if(mWindowType==eWindowType_plugin_ipc_chrome){returnNS_OK;}for(uint32_ti=0;i<aConfigurations.Length();++i){constConfiguration&configuration=aConfigurations[i];auto*w=static_cast<nsWindow*>(configuration.mChild.get());NS_ASSERTION(w->GetParent()==this,"Configured widget is not a child");w->SetWindowClipRegion(configuration.mClipRegion,true);if(w->mBounds.Size()!=configuration.mBounds.Size()){w->Resize(configuration.mBounds.x,configuration.mBounds.y,configuration.mBounds.width,configuration.mBounds.height,true);}elseif(w->mBounds.TopLeft()!=configuration.mBounds.TopLeft()){w->Move(configuration.mBounds.x,configuration.mBounds.y);}w->SetWindowClipRegion(configuration.mClipRegion,false);}returnNS_OK;}nsresultnsWindow::SetWindowClipRegion(constnsTArray<LayoutDeviceIntRect>&aRects,boolaIntersectWithExisting){constnsTArray<LayoutDeviceIntRect>*newRects=&aRects;AutoTArray<LayoutDeviceIntRect,1>intersectRects;if(aIntersectWithExisting){AutoTArray<LayoutDeviceIntRect,1>existingRects;GetWindowClipRegion(&existingRects);LayoutDeviceIntRegionexistingRegion=RegionFromArray(existingRects);LayoutDeviceIntRegionnewRegion=RegionFromArray(aRects);LayoutDeviceIntRegionintersectRegion;intersectRegion.And(newRegion,existingRegion);// If mClipRects is null we haven't set a clip rect yet, so we// need to set the clip even if it is equal.if(mClipRects&&intersectRegion.IsEqual(existingRegion)){returnNS_OK;}if(!intersectRegion.IsEqual(newRegion)){ArrayFromRegion(intersectRegion,intersectRects);newRects=&intersectRects;}}if(IsWindowClipRegionEqual(*newRects))returnNS_OK;StoreWindowClipRegion(*newRects);if(!mGdkWindow)returnNS_OK;#if (MOZ_WIDGET_GTK == 2)GdkRegion*region=gdk_region_new();// aborts on OOMfor(uint32_ti=0;i<newRects->Length();++i){constLayoutDeviceIntRect&r=newRects->ElementAt(i);GdkRectanglerect={r.x,r.y,r.width,r.height};gdk_region_union_with_rect(region,&rect);}gdk_window_shape_combine_region(mGdkWindow,region,0,0);gdk_region_destroy(region);#elsecairo_region_t*region=cairo_region_create();for(uint32_ti=0;i<newRects->Length();++i){constLayoutDeviceIntRect&r=newRects->ElementAt(i);cairo_rectangle_int_trect={r.x,r.y,r.width,r.height};cairo_region_union_rectangle(region,&rect);}gdk_window_shape_combine_region(mGdkWindow,region,0,0);cairo_region_destroy(region);#endifreturnNS_OK;}voidnsWindow::ResizeTransparencyBitmap(){if(!mTransparencyBitmap)return;if(mBounds.width==mTransparencyBitmapWidth&&mBounds.height==mTransparencyBitmapHeight)return;int32_tnewRowBytes=GetBitmapStride(mBounds.width);int32_tnewSize=newRowBytes*mBounds.height;auto*newBits=newgchar[newSize];// fill new mask with "transparent", firstmemset(newBits,0,newSize);// Now copy the intersection of the old and new areas into the new maskint32_tcopyWidth=std::min(mBounds.width,mTransparencyBitmapWidth);int32_tcopyHeight=std::min(mBounds.height,mTransparencyBitmapHeight);int32_toldRowBytes=GetBitmapStride(mTransparencyBitmapWidth);int32_tcopyBytes=GetBitmapStride(copyWidth);int32_ti;gchar*fromPtr=mTransparencyBitmap;gchar*toPtr=newBits;for(i=0;i<copyHeight;i++){memcpy(toPtr,fromPtr,copyBytes);fromPtr+=oldRowBytes;toPtr+=newRowBytes;}delete[]mTransparencyBitmap;mTransparencyBitmap=newBits;mTransparencyBitmapWidth=mBounds.width;mTransparencyBitmapHeight=mBounds.height;}staticboolChangedMaskBits(gchar*aMaskBits,int32_taMaskWidth,int32_taMaskHeight,constnsIntRect&aRect,uint8_t*aAlphas,int32_taStride){int32_tx,y,xMax=aRect.XMost(),yMax=aRect.YMost();int32_tmaskBytesPerRow=GetBitmapStride(aMaskWidth);for(y=aRect.y;y<yMax;y++){gchar*maskBytes=aMaskBits+y*maskBytesPerRow;uint8_t*alphas=aAlphas;for(x=aRect.x;x<xMax;x++){boolnewBit=*alphas>0x7f;alphas++;gcharmaskByte=maskBytes[x>>3];boolmaskBit=(maskByte&(1<<(x&7)))!=0;if(maskBit!=newBit){returntrue;}}aAlphas+=aStride;}returnfalse;}staticvoidUpdateMaskBits(gchar*aMaskBits,int32_taMaskWidth,int32_taMaskHeight,constnsIntRect&aRect,uint8_t*aAlphas,int32_taStride){int32_tx,y,xMax=aRect.XMost(),yMax=aRect.YMost();int32_tmaskBytesPerRow=GetBitmapStride(aMaskWidth);for(y=aRect.y;y<yMax;y++){gchar*maskBytes=aMaskBits+y*maskBytesPerRow;uint8_t*alphas=aAlphas;for(x=aRect.x;x<xMax;x++){boolnewBit=*alphas>0x7f;alphas++;gcharmask=1<<(x&7);gcharmaskByte=maskBytes[x>>3];// Note: '-newBit' turns 0 into 00...00 and 1 into 11...11maskBytes[x>>3]=(maskByte&~mask)|(-newBit&mask);}aAlphas+=aStride;}}voidnsWindow::ApplyTransparencyBitmap(){#ifdef MOZ_X11// We use X11 calls where possible, because GDK handles expose events// for shaped windows in a way that's incompatible with us (Bug 635903).// It doesn't occur when the shapes are set through X.Display*xDisplay=GDK_WINDOW_XDISPLAY(mGdkWindow);WindowxDrawable=GDK_WINDOW_XID(mGdkWindow);PixmapmaskPixmap=XCreateBitmapFromData(xDisplay,xDrawable,mTransparencyBitmap,mTransparencyBitmapWidth,mTransparencyBitmapHeight);XShapeCombineMask(xDisplay,xDrawable,ShapeBounding,0,0,maskPixmap,ShapeSet);XFreePixmap(xDisplay,maskPixmap);#else#if (MOZ_WIDGET_GTK == 2)gtk_widget_reset_shapes(mShell);GdkBitmap*maskBitmap=gdk_bitmap_create_from_data(mGdkWindow,mTransparencyBitmap,mTransparencyBitmapWidth,mTransparencyBitmapHeight);if(!maskBitmap)return;gtk_widget_shape_combine_mask(mShell,maskBitmap,0,0);g_object_unref(maskBitmap);#elsecairo_surface_t*maskBitmap;maskBitmap=cairo_image_surface_create_for_data((unsignedchar*)mTransparencyBitmap,CAIRO_FORMAT_A1,mTransparencyBitmapWidth,mTransparencyBitmapHeight,GetBitmapStride(mTransparencyBitmapWidth));if(!maskBitmap)return;cairo_region_t*maskRegion=gdk_cairo_region_create_from_surface(maskBitmap);gtk_widget_shape_combine_region(mShell,maskRegion);cairo_region_destroy(maskRegion);cairo_surface_destroy(maskBitmap);#endif // MOZ_WIDGET_GTK == 2#endif // MOZ_X11}voidnsWindow::ClearTransparencyBitmap(){if(!mTransparencyBitmap)return;delete[]mTransparencyBitmap;mTransparencyBitmap=nullptr;mTransparencyBitmapWidth=0;mTransparencyBitmapHeight=0;if(!mShell)return;#ifdef MOZ_X11if(!mGdkWindow)return;Display*xDisplay=GDK_WINDOW_XDISPLAY(mGdkWindow);WindowxWindow=gdk_x11_window_get_xid(mGdkWindow);XShapeCombineMask(xDisplay,xWindow,ShapeBounding,0,0,X11None,ShapeSet);#endif}nsresultnsWindow::UpdateTranslucentWindowAlphaInternal(constnsIntRect&aRect,uint8_t*aAlphas,int32_taStride){if(!mShell){// Pass the request to the toplevel windowGtkWidget*topWidget=GetToplevelWidget();if(!topWidget)returnNS_ERROR_FAILURE;nsWindow*topWindow=get_window_for_gtk_widget(topWidget);if(!topWindow)returnNS_ERROR_FAILURE;returntopWindow->UpdateTranslucentWindowAlphaInternal(aRect,aAlphas,aStride);}NS_ASSERTION(mIsTransparent,"Window is not transparent");if(mTransparencyBitmap==nullptr){int32_tsize=GetBitmapStride(mBounds.width)*mBounds.height;mTransparencyBitmap=newgchar[size];memset(mTransparencyBitmap,255,size);mTransparencyBitmapWidth=mBounds.width;mTransparencyBitmapHeight=mBounds.height;}else{ResizeTransparencyBitmap();}nsIntRectrect;rect.IntersectRect(aRect,nsIntRect(0,0,mBounds.width,mBounds.height));if(!ChangedMaskBits(mTransparencyBitmap,mBounds.width,mBounds.height,rect,aAlphas,aStride))// skip the expensive stuff if the mask bits haven't changed; hopefully// this is the common casereturnNS_OK;UpdateMaskBits(mTransparencyBitmap,mBounds.width,mBounds.height,rect,aAlphas,aStride);if(!mNeedsShow){ApplyTransparencyBitmap();}returnNS_OK;}voidnsWindow::GrabPointer(guint32aTime){LOG(("GrabPointer time=0x%08x retry=%d\n",(unsignedint)aTime,mRetryPointerGrab));mRetryPointerGrab=false;sRetryGrabTime=aTime;// If the window isn't visible, just set the flag to retry the// grab. When this window becomes visible, the grab will be// retried.if(!mHasMappedToplevel||mIsFullyObscured){LOG(("GrabPointer: window not visible\n"));mRetryPointerGrab=true;return;}if(!mGdkWindow)return;gintretval;retval=gdk_pointer_grab(mGdkWindow,TRUE,(GdkEventMask)(GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK|GDK_POINTER_MOTION_MASK),(GdkWindow*)nullptr,nullptr,aTime);if(retval==GDK_GRAB_NOT_VIEWABLE){LOG(("GrabPointer: window not viewable; will retry\n"));mRetryPointerGrab=true;}elseif(retval!=GDK_GRAB_SUCCESS){LOG(("GrabPointer: pointer grab failed: %i\n",retval));// A failed grab indicates that another app has grabbed the pointer.// Check for rollup now, because, without the grab, we likely won't// get subsequent button press events. Do this with an event so that// popups don't rollup while potentially adjusting the grab for// this popup.nsCOMPtr<nsIRunnable>event=NewRunnableMethod("nsWindow::CheckForRollupDuringGrab",this,&nsWindow::CheckForRollupDuringGrab);NS_DispatchToCurrentThread(event.forget());}}voidnsWindow::ReleaseGrabs(void){LOG(("ReleaseGrabs\n"));mRetryPointerGrab=false;gdk_pointer_ungrab(GDK_CURRENT_TIME);}GtkWidget*nsWindow::GetToplevelWidget(){if(mShell){returnmShell;}GtkWidget*widget=GetMozContainerWidget();if(!widget)returnnullptr;returngtk_widget_get_toplevel(widget);}GtkWidget*nsWindow::GetMozContainerWidget(){if(!mGdkWindow)returnnullptr;if(mContainer)returnGTK_WIDGET(mContainer);GtkWidget*owningWidget=get_gtk_widget_for_gdk_window(mGdkWindow);returnowningWidget;}nsWindow*nsWindow::GetContainerWindow(){GtkWidget*owningWidget=GetMozContainerWidget();if(!owningWidget)returnnullptr;nsWindow*window=get_window_for_gtk_widget(owningWidget);NS_ASSERTION(window,"No nsWindow for container widget");returnwindow;}voidnsWindow::SetUrgencyHint(GtkWidget*top_window,boolstate){if(!top_window)return;gdk_window_set_urgency_hint(gtk_widget_get_window(top_window),state);}voidnsWindow::SetDefaultIcon(void){SetIcon(NS_LITERAL_STRING("default"));}gintnsWindow::ConvertBorderStyles(nsBorderStyleaStyle){gintw=0;if(aStyle==eBorderStyle_default)return-1;// note that we don't handle eBorderStyle_close yetif(aStyle&eBorderStyle_all)w|=GDK_DECOR_ALL;if(aStyle&eBorderStyle_border)w|=GDK_DECOR_BORDER;if(aStyle&eBorderStyle_resizeh)w|=GDK_DECOR_RESIZEH;if(aStyle&eBorderStyle_title)w|=GDK_DECOR_TITLE;if(aStyle&eBorderStyle_menu)w|=GDK_DECOR_MENU;if(aStyle&eBorderStyle_minimize)w|=GDK_DECOR_MINIMIZE;if(aStyle&eBorderStyle_maximize)w|=GDK_DECOR_MAXIMIZE;returnw;}classFullscreenTransitionWindowfinal:publicnsISupports{public:NS_DECL_ISUPPORTSexplicitFullscreenTransitionWindow(GtkWidget*aWidget);GtkWidget*mWindow;private:~FullscreenTransitionWindow();};NS_IMPL_ISUPPORTS0(FullscreenTransitionWindow)FullscreenTransitionWindow::FullscreenTransitionWindow(GtkWidget*aWidget){mWindow=gtk_window_new(GTK_WINDOW_POPUP);GtkWindow*gtkWin=GTK_WINDOW(mWindow);gtk_window_set_type_hint(gtkWin,GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);gtk_window_set_transient_for(gtkWin,GTK_WINDOW(aWidget));gtk_window_set_decorated(gtkWin,false);GdkWindow*gdkWin=gtk_widget_get_window(aWidget);GdkScreen*screen=gtk_widget_get_screen(aWidget);gintmonitorNum=gdk_screen_get_monitor_at_window(screen,gdkWin);GdkRectanglemonitorRect;gdk_screen_get_monitor_geometry(screen,monitorNum,&monitorRect);gtk_window_set_screen(gtkWin,screen);gtk_window_move(gtkWin,monitorRect.x,monitorRect.y);gtk_window_resize(gtkWin,monitorRect.width,monitorRect.height);GdkColorbgColor;bgColor.red=bgColor.green=bgColor.blue=0;gtk_widget_modify_bg(mWindow,GTK_STATE_NORMAL,&bgColor);gtk_window_set_opacity(gtkWin,0.0);gtk_widget_show(mWindow);}FullscreenTransitionWindow::~FullscreenTransitionWindow(){gtk_widget_destroy(mWindow);}classFullscreenTransitionData{public:FullscreenTransitionData(nsIWidget::FullscreenTransitionStageaStage,uint16_taDuration,nsIRunnable*aCallback,FullscreenTransitionWindow*aWindow):mStage(aStage),mStartTime(TimeStamp::Now()),mDuration(TimeDuration::FromMilliseconds(aDuration)),mCallback(aCallback),mWindow(aWindow){}staticconstguintsInterval=1000/30;// 30fpsstaticgbooleanTimeoutCallback(gpointeraData);private:nsIWidget::FullscreenTransitionStagemStage;TimeStampmStartTime;TimeDurationmDuration;nsCOMPtr<nsIRunnable>mCallback;RefPtr<FullscreenTransitionWindow>mWindow;};/* static */gbooleanFullscreenTransitionData::TimeoutCallback(gpointeraData){boolfinishing=false;autodata=static_cast<FullscreenTransitionData*>(aData);gdoubleopacity=(TimeStamp::Now()-data->mStartTime)/data->mDuration;if(opacity>=1.0){opacity=1.0;finishing=true;}if(data->mStage==nsIWidget::eAfterFullscreenToggle){opacity=1.0-opacity;}gtk_window_set_opacity(GTK_WINDOW(data->mWindow->mWindow),opacity);if(!finishing){returnTRUE;}NS_DispatchToMainThread(data->mCallback.forget());deletedata;returnFALSE;}/* virtual */boolnsWindow::PrepareForFullscreenTransition(nsISupports**aData){GdkScreen*screen=gtk_widget_get_screen(mShell);if(!gdk_screen_is_composited(screen)){returnfalse;}*aData=do_AddRef(newFullscreenTransitionWindow(mShell)).take();returntrue;}/* virtual */voidnsWindow::PerformFullscreenTransition(FullscreenTransitionStageaStage,uint16_taDuration,nsISupports*aData,nsIRunnable*aCallback){autodata=static_cast<FullscreenTransitionWindow*>(aData);// This will be released at the end of the last timeout callback for it.autotransitionData=newFullscreenTransitionData(aStage,aDuration,aCallback,data);g_timeout_add_full(G_PRIORITY_HIGH,FullscreenTransitionData::sInterval,FullscreenTransitionData::TimeoutCallback,transitionData,nullptr);}already_AddRefed<nsIScreen>nsWindow::GetWidgetScreen(){nsCOMPtr<nsIScreenManager>screenManager;screenManager=do_GetService("@mozilla.org/gfx/screenmanager;1");if(!screenManager){returnnullptr;}// GetScreenBounds() is slow for the GTK port so we override and use// mBounds directly.LayoutDeviceIntRectbounds=mBounds;if(!mIsTopLevel){bounds.MoveTo(WidgetToScreenOffset());}DesktopIntRectdeskBounds=RoundedToInt(bounds/GetDesktopToDeviceScale());nsCOMPtr<nsIScreen>screen;screenManager->ScreenForRect(deskBounds.x,deskBounds.y,deskBounds.width,deskBounds.height,getter_AddRefs(screen));returnscreen.forget();}staticboolIsFullscreenSupported(GtkWidget*aShell){#ifdef MOZ_X11GdkScreen*screen=gtk_widget_get_screen(aShell);GdkAtomatom=gdk_atom_intern("_NET_WM_STATE_FULLSCREEN",FALSE);if(!gdk_x11_screen_supports_net_wm_hint(screen,atom)){returnfalse;}#endifreturntrue;}nsresultnsWindow::MakeFullScreen(boolaFullScreen,nsIScreen*aTargetScreen){LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",(void*)this,aFullScreen));if(!IsFullscreenSupported(mShell)){returnNS_ERROR_NOT_AVAILABLE;}if(aFullScreen){if(mSizeMode!=nsSizeMode_Fullscreen)mLastSizeMode=mSizeMode;mSizeMode=nsSizeMode_Fullscreen;gtk_window_fullscreen(GTK_WINDOW(mShell));}else{mSizeMode=mLastSizeMode;gtk_window_unfullscreen(GTK_WINDOW(mShell));}NS_ASSERTION(mLastSizeMode!=nsSizeMode_Fullscreen,"mLastSizeMode should never be fullscreen");returnNS_OK;}voidnsWindow::HideWindowChrome(boolaShouldHide){if(!mShell){// Pass the request to the toplevel windowGtkWidget*topWidget=GetToplevelWidget();if(!topWidget)return;nsWindow*topWindow=get_window_for_gtk_widget(topWidget);if(!topWindow)return;topWindow->HideWindowChrome(aShouldHide);return;}// Sawfish, metacity, and presumably other window managers get// confused if we change the window decorations while the window// is visible.boolwasVisible=false;if(gdk_window_is_visible(mGdkWindow)){gdk_window_hide(mGdkWindow);wasVisible=true;}gintwmd;if(aShouldHide)wmd=0;elsewmd=ConvertBorderStyles(mBorderStyle);if(wmd!=-1)gdk_window_set_decorations(mGdkWindow,(GdkWMDecoration)wmd);if(wasVisible)gdk_window_show(mGdkWindow);// For some window managers, adding or removing window decorations// requires unmapping and remapping our toplevel window. Go ahead// and flush the queue here so that we don't end up with a BadWindow// error later when this happens (when the persistence timer fires// and GetWindowPos is called)#ifdef MOZ_X11XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),False);#elsegdk_flush();#endif /* MOZ_X11 */}boolnsWindow::CheckForRollup(gdoubleaMouseX,gdoubleaMouseY,boolaIsWheel,boolaAlwaysRollup){nsIRollupListener*rollupListener=GetActiveRollupListener();nsCOMPtr<nsIWidget>rollupWidget;if(rollupListener){rollupWidget=rollupListener->GetRollupWidget();}if(!rollupWidget){nsBaseWidget::gRollupListener=nullptr;returnfalse;}boolretVal=false;auto*currentPopup=(GdkWindow*)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);if(aAlwaysRollup||!is_mouse_in_window(currentPopup,aMouseX,aMouseY)){boolrollup=true;if(aIsWheel){rollup=rollupListener->ShouldRollupOnMouseWheelEvent();retVal=rollupListener->ShouldConsumeOnMouseWheelEvent();}// if we're dealing with menus, we probably have submenus and// we don't want to rollup if the click is in a parent menu of// the current submenuuint32_tpopupsToRollup=UINT32_MAX;if(!aAlwaysRollup){AutoTArray<nsIWidget*,5>widgetChain;uint32_tsameTypeCount=rollupListener->GetSubmenuWidgetChain(&widgetChain);for(uint32_ti=0;i<widgetChain.Length();++i){nsIWidget*widget=widgetChain[i];auto*currWindow=(GdkWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);if(is_mouse_in_window(currWindow,aMouseX,aMouseY)){// don't roll up if the mouse event occurred within a// menu of the same type. If the mouse event occurred// in a menu higher than that, roll up, but pass the// number of popups to Rollup so that only those of the// same type close up.if(i<sameTypeCount){rollup=false;}else{popupsToRollup=sameTypeCount;}break;}}// foreach parent menu widget}// if rollup listener knows about menus// if we've determined that we should still rollup, do it.boolusePoint=!aIsWheel&&!aAlwaysRollup;IntPointpoint=IntPoint::Truncate(aMouseX,aMouseY);if(rollup&&rollupListener->Rollup(popupsToRollup,true,usePoint?&point:nullptr,nullptr)){retVal=true;}}returnretVal;}/* static */boolnsWindow::DragInProgress(void){nsCOMPtr<nsIDragService>dragService=do_GetService(kCDragServiceCID);if(!dragService)returnfalse;nsCOMPtr<nsIDragSession>currentDragSession;dragService->GetCurrentSession(getter_AddRefs(currentDragSession));returncurrentDragSession!=nullptr;}staticboolis_mouse_in_window(GdkWindow*aWindow,gdoubleaMouseX,gdoubleaMouseY){gintx=0;ginty=0;gintw,h;gintoffsetX=0;gintoffsetY=0;GdkWindow*window=aWindow;while(window){ginttmpX=0;ginttmpY=0;gdk_window_get_position(window,&tmpX,&tmpY);GtkWidget*widget=get_gtk_widget_for_gdk_window(window);// if this is a window, compute x and y given its origin and our// offsetif(GTK_IS_WINDOW(widget)){x=tmpX+offsetX;y=tmpY+offsetY;break;}offsetX+=tmpX;offsetY+=tmpY;window=gdk_window_get_parent(window);}#if (MOZ_WIDGET_GTK == 2)gdk_drawable_get_size(aWindow,&w,&h);#elsew=gdk_window_get_width(aWindow);h=gdk_window_get_height(aWindow);#endifif(aMouseX>x&&aMouseX<x+w&&aMouseY>y&&aMouseY<y+h)returntrue;returnfalse;}staticnsWindow*get_window_for_gtk_widget(GtkWidget*widget){gpointeruser_data=g_object_get_data(G_OBJECT(widget),"nsWindow");returnstatic_cast<nsWindow*>(user_data);}staticnsWindow*get_window_for_gdk_window(GdkWindow*window){gpointeruser_data=g_object_get_data(G_OBJECT(window),"nsWindow");returnstatic_cast<nsWindow*>(user_data);}staticGtkWidget*get_gtk_widget_for_gdk_window(GdkWindow*window){gpointeruser_data=nullptr;gdk_window_get_user_data(window,&user_data);returnGTK_WIDGET(user_data);}staticGdkCursor*get_gtk_cursor(nsCursoraCursor){GdkCursor*gdkcursor=nullptr;uint8_tnewType=0xff;if((gdkcursor=gCursorCache[aCursor])){returngdkcursor;}GdkDisplay*defaultDisplay=gdk_display_get_default();// The strategy here is to use standard GDK cursors, and, if not available,// load by standard name with gdk_cursor_new_from_name.// Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/switch(aCursor){caseeCursor_standard:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_LEFT_PTR);break;caseeCursor_wait:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_WATCH);break;caseeCursor_select:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_XTERM);break;caseeCursor_hyperlink:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_HAND2);break;caseeCursor_n_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_TOP_SIDE);break;caseeCursor_s_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_BOTTOM_SIDE);break;caseeCursor_w_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_LEFT_SIDE);break;caseeCursor_e_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_RIGHT_SIDE);break;caseeCursor_nw_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_TOP_LEFT_CORNER);break;caseeCursor_se_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_BOTTOM_RIGHT_CORNER);break;caseeCursor_ne_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_TOP_RIGHT_CORNER);break;caseeCursor_sw_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_BOTTOM_LEFT_CORNER);break;caseeCursor_crosshair:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_CROSSHAIR);break;caseeCursor_move:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_FLEUR);break;caseeCursor_help:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_QUESTION_ARROW);break;caseeCursor_copy:// CSS3gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"copy");if(!gdkcursor)newType=MOZ_CURSOR_COPY;break;caseeCursor_alias:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"alias");if(!gdkcursor)newType=MOZ_CURSOR_ALIAS;break;caseeCursor_context_menu:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"context-menu");if(!gdkcursor)newType=MOZ_CURSOR_CONTEXT_MENU;break;caseeCursor_cell:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_PLUS);break;// Those two aren’t standardized. Trying both KDE’s and GNOME’s namescaseeCursor_grab:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"openhand");if(!gdkcursor)newType=MOZ_CURSOR_HAND_GRAB;break;caseeCursor_grabbing:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"closedhand");if(!gdkcursor)gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"grabbing");if(!gdkcursor)newType=MOZ_CURSOR_HAND_GRABBING;break;caseeCursor_spinning:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"progress");if(!gdkcursor)newType=MOZ_CURSOR_SPINNING;break;caseeCursor_zoom_in:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"zoom-in");if(!gdkcursor)newType=MOZ_CURSOR_ZOOM_IN;break;caseeCursor_zoom_out:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"zoom-out");if(!gdkcursor)newType=MOZ_CURSOR_ZOOM_OUT;break;caseeCursor_not_allowed:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"not-allowed");if(!gdkcursor)// nonstandard, yet commongdkcursor=gdk_cursor_new_from_name(defaultDisplay,"crossed_circle");if(!gdkcursor)newType=MOZ_CURSOR_NOT_ALLOWED;break;caseeCursor_no_drop:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"no-drop");if(!gdkcursor)// this nonstandard sequence makes it work on KDE and GNOMEgdkcursor=gdk_cursor_new_from_name(defaultDisplay,"forbidden");if(!gdkcursor)gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"circle");if(!gdkcursor)newType=MOZ_CURSOR_NOT_ALLOWED;break;caseeCursor_vertical_text:newType=MOZ_CURSOR_VERTICAL_TEXT;break;caseeCursor_all_scroll:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_FLEUR);break;caseeCursor_nesw_resize:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"size_bdiag");if(!gdkcursor)newType=MOZ_CURSOR_NESW_RESIZE;break;caseeCursor_nwse_resize:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"size_fdiag");if(!gdkcursor)newType=MOZ_CURSOR_NWSE_RESIZE;break;caseeCursor_ns_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_SB_V_DOUBLE_ARROW);break;caseeCursor_ew_resize:gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_SB_H_DOUBLE_ARROW);break;// Here, two better fitting cursors exist in some cursor themes. Try those firstcaseeCursor_row_resize:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"split_v");if(!gdkcursor)gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_SB_V_DOUBLE_ARROW);break;caseeCursor_col_resize:gdkcursor=gdk_cursor_new_from_name(defaultDisplay,"split_h");if(!gdkcursor)gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_SB_H_DOUBLE_ARROW);break;caseeCursor_none:newType=MOZ_CURSOR_NONE;break;default:NS_ASSERTION(aCursor,"Invalid cursor type");gdkcursor=gdk_cursor_new_for_display(defaultDisplay,GDK_LEFT_PTR);break;}// If by now we don't have a xcursor, this means we have to make a custom// one. First, we try creating a named cursor based on the hash of our// custom bitmap, as libXcursor has some magic to convert bitmapped cursors// to themed cursorsif(newType!=0xFF&&GtkCursors[newType].hash){gdkcursor=gdk_cursor_new_from_name(defaultDisplay,GtkCursors[newType].hash);}// If we still don't have a xcursor, we now really create a bitmap cursorif(newType!=0xff&&!gdkcursor){GdkPixbuf*cursor_pixbuf=gdk_pixbuf_new(GDK_COLORSPACE_RGB,TRUE,8,32,32);if(!cursor_pixbuf)returnnullptr;guchar*data=gdk_pixbuf_get_pixels(cursor_pixbuf);// Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask// GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)// so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).constunsignedchar*bits=GtkCursors[newType].bits;constunsignedchar*mask_bits=GtkCursors[newType].mask_bits;for(inti=0;i<128;i++){charbit=*bits++;charmask=*mask_bits++;for(intj=0;j<8;j++){unsignedcharpix=~(((bit>>j)&0x01)*0xff);*data++=pix;*data++=pix;*data++=pix;*data++=(((mask>>j)&0x01)*0xff);}}gdkcursor=gdk_cursor_new_from_pixbuf(gdk_display_get_default(),cursor_pixbuf,GtkCursors[newType].hot_x,GtkCursors[newType].hot_y);g_object_unref(cursor_pixbuf);}gCursorCache[aCursor]=gdkcursor;returngdkcursor;}// gtk callbacks#if (MOZ_WIDGET_GTK == 2)staticgbooleanexpose_event_cb(GtkWidget*widget,GdkEventExpose*event){RefPtr<nsWindow>window=get_window_for_gdk_window(event->window);if(!window)returnFALSE;window->OnExposeEvent(event);returnFALSE;}#elsevoiddraw_window_of_widget(GtkWidget*widget,GdkWindow*aWindow,cairo_t*cr){if(gtk_cairo_should_draw_window(cr,aWindow)){RefPtr<nsWindow>window=get_window_for_gdk_window(aWindow);if(!window){NS_WARNING("Cannot get nsWindow from GtkWidget");}else{cairo_save(cr);gtk_cairo_transform_to_window(cr,widget,aWindow);// TODO - window->OnExposeEvent() can destroy this or other windows,// do we need to handle it somehow?window->OnExposeEvent(cr);cairo_restore(cr);}}GList*children=gdk_window_get_children(aWindow);GList*child=children;while(child){GdkWindow*window=GDK_WINDOW(child->data);gpointerwindowWidget;gdk_window_get_user_data(window,&windowWidget);if(windowWidget==widget){draw_window_of_widget(widget,window,cr);}child=g_list_next(child);}g_list_free(children);}/* static */gbooleanexpose_event_cb(GtkWidget*widget,cairo_t*cr){draw_window_of_widget(widget,gtk_widget_get_window(widget),cr);// A strong reference is already held during "draw" signal emission,// but GTK+ 3.4 wants the object to live a little longer than that// (bug 1225970).g_object_ref(widget);g_idle_add([](gpointerdata)->gboolean{g_object_unref(data);returnG_SOURCE_REMOVE;},widget);returnFALSE;}#endif //MOZ_WIDGET_GTK == 2staticgbooleanconfigure_event_cb(GtkWidget*widget,GdkEventConfigure*event){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;returnwindow->OnConfigureEvent(widget,event);}staticvoidcontainer_unrealize_cb(GtkWidget*widget){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)return;window->OnContainerUnrealize();}staticvoidsize_allocate_cb(GtkWidget*widget,GtkAllocation*allocation){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)return;window->OnSizeAllocate(allocation);}staticgbooleandelete_event_cb(GtkWidget*widget,GdkEventAny*event){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;window->OnDeleteEvent();returnTRUE;}staticgbooleanenter_notify_event_cb(GtkWidget*widget,GdkEventCrossing*event){RefPtr<nsWindow>window=get_window_for_gdk_window(event->window);if(!window)returnTRUE;window->OnEnterNotifyEvent(event);returnTRUE;}staticgbooleanleave_notify_event_cb(GtkWidget*widget,GdkEventCrossing*event){if(is_parent_grab_leave(event)){returnTRUE;}// bug 369599: Suppress LeaveNotify events caused by pointer grabs to// avoid generating spurious mouse exit events.autox=gint(event->x_root);autoy=gint(event->y_root);GdkDisplay*display=gtk_widget_get_display(widget);GdkWindow*winAtPt=gdk_display_get_window_at_pointer(display,&x,&y);if(winAtPt==event->window){returnTRUE;}RefPtr<nsWindow>window=get_window_for_gdk_window(event->window);if(!window)returnTRUE;window->OnLeaveNotifyEvent(event);returnTRUE;}staticnsWindow*GetFirstNSWindowForGDKWindow(GdkWindow*aGdkWindow){nsWindow*window;while(!(window=get_window_for_gdk_window(aGdkWindow))){// The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,// but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at// event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.aGdkWindow=gdk_window_get_parent(aGdkWindow);if(!aGdkWindow){window=nullptr;break;}}returnwindow;}staticgbooleanmotion_notify_event_cb(GtkWidget*widget,GdkEventMotion*event){UpdateLastInputEventTime(event);nsWindow*window=GetFirstNSWindowForGDKWindow(event->window);if(!window)returnFALSE;window->OnMotionNotifyEvent(event);returnTRUE;}staticgbooleanbutton_press_event_cb(GtkWidget*widget,GdkEventButton*event){UpdateLastInputEventTime(event);nsWindow*window=GetFirstNSWindowForGDKWindow(event->window);if(!window)returnFALSE;window->OnButtonPressEvent(event);returnTRUE;}staticgbooleanbutton_release_event_cb(GtkWidget*widget,GdkEventButton*event){UpdateLastInputEventTime(event);nsWindow*window=GetFirstNSWindowForGDKWindow(event->window);if(!window)returnFALSE;window->OnButtonReleaseEvent(event);returnTRUE;}staticgbooleanfocus_in_event_cb(GtkWidget*widget,GdkEventFocus*event){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;window->OnContainerFocusInEvent(event);returnFALSE;}staticgbooleanfocus_out_event_cb(GtkWidget*widget,GdkEventFocus*event){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;window->OnContainerFocusOutEvent(event);returnFALSE;}#ifdef MOZ_X11// For long-lived popup windows that don't really take focus themselves but// may have elements that accept keyboard input when the parent window is// active, focus is handled specially. These windows include noautohide// panels. (This special handling is not necessary for temporary popups where// the keyboard is grabbed.)//// Mousing over or clicking on these windows should not cause them to steal// focus from their parent windows, so, the input field of WM_HINTS is set to// False to request that the window manager not set the input focus to this// window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7//// However, these windows can still receive WM_TAKE_FOCUS messages from the// window manager, so they can still detect when the user has indicated that// they wish to direct keyboard input at these windows. When the window// manager offers focus to these windows (after a mouse over or click, for// example), a request to make the parent window active is issued. When the// parent window becomes active, keyboard events will be received.staticGdkFilterReturnpopup_take_focus_filter(GdkXEvent*gdk_xevent,GdkEvent*event,gpointerdata){auto*xevent=static_cast<XEvent*>(gdk_xevent);if(xevent->type!=ClientMessage)returnGDK_FILTER_CONTINUE;XClientMessageEvent&xclient=xevent->xclient;if(xclient.message_type!=gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))returnGDK_FILTER_CONTINUE;Atomatom=xclient.data.l[0];if(atom!=gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))returnGDK_FILTER_CONTINUE;guint32timestamp=xclient.data.l[1];GtkWidget*widget=get_gtk_widget_for_gdk_window(event->any.window);if(!widget)returnGDK_FILTER_CONTINUE;GtkWindow*parent=gtk_window_get_transient_for(GTK_WINDOW(widget));if(!parent)returnGDK_FILTER_CONTINUE;if(gtk_window_is_active(parent))returnGDK_FILTER_REMOVE;// leave input focus on the parentGdkWindow*parent_window=gtk_widget_get_window(GTK_WIDGET(parent));if(!parent_window)returnGDK_FILTER_CONTINUE;// In case the parent has not been deconified.gdk_window_show_unraised(parent_window);// Request focus on the parent window.// Use gdk_window_focus rather than gtk_window_present to avoid// raising the parent window.gdk_window_focus(parent_window,timestamp);returnGDK_FILTER_REMOVE;}#endif /* MOZ_X11 */staticgbooleankey_press_event_cb(GtkWidget*widget,GdkEventKey*event){LOG(("key_press_event_cb\n"));UpdateLastInputEventTime(event);// find the window with focus and dispatch this event to that widgetnsWindow*window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;RefPtr<nsWindow>focusWindow=gFocusWindow?gFocusWindow:window;#ifdef MOZ_X11// Keyboard repeat can cause key press events to queue up when there are// slow event handlers (bug 301029). Throttle these events by removing// consecutive pending duplicate KeyPress events to the same window.// We use the event time of the last one.// Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events// are generated only when the key is physically released.#define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */GdkDisplay*gdkDisplay=gtk_widget_get_display(widget);if(GDK_IS_X11_DISPLAY(gdkDisplay)){Display*dpy=GDK_DISPLAY_XDISPLAY(gdkDisplay);while(XPending(dpy)){XEventnext_event;XPeekEvent(dpy,&next_event);GdkWindow*nextGdkWindow=gdk_x11_window_lookup_for_display(gdkDisplay,next_event.xany.window);if(nextGdkWindow!=event->window||next_event.type!=KeyPress||next_event.xkey.keycode!=event->hardware_keycode||next_event.xkey.state!=(event->state&NS_GDKEVENT_MATCH_MASK)){break;}XNextEvent(dpy,&next_event);event->time=next_event.xkey.time;}}#endifreturnfocusWindow->OnKeyPressEvent(event);}staticgbooleankey_release_event_cb(GtkWidget*widget,GdkEventKey*event){LOG(("key_release_event_cb\n"));UpdateLastInputEventTime(event);// find the window with focus and dispatch this event to that widgetnsWindow*window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;RefPtr<nsWindow>focusWindow=gFocusWindow?gFocusWindow:window;returnfocusWindow->OnKeyReleaseEvent(event);}staticgbooleanproperty_notify_event_cb(GtkWidget*aWidget,GdkEventProperty*aEvent){RefPtr<nsWindow>window=get_window_for_gdk_window(aEvent->window);if(!window)returnFALSE;returnwindow->OnPropertyNotifyEvent(aWidget,aEvent);}staticgbooleanscroll_event_cb(GtkWidget*widget,GdkEventScroll*event){nsWindow*window=GetFirstNSWindowForGDKWindow(event->window);if(!window)returnFALSE;window->OnScrollEvent(event);returnTRUE;}staticgbooleanvisibility_notify_event_cb(GtkWidget*widget,GdkEventVisibility*event){RefPtr<nsWindow>window=get_window_for_gdk_window(event->window);if(!window)returnFALSE;window->OnVisibilityNotifyEvent(event);returnTRUE;}staticvoidhierarchy_changed_cb(GtkWidget*widget,GtkWidget*previous_toplevel){GtkWidget*toplevel=gtk_widget_get_toplevel(widget);GdkWindowStateold_window_state=GDK_WINDOW_STATE_WITHDRAWN;GdkEventWindowStateevent;event.new_window_state=GDK_WINDOW_STATE_WITHDRAWN;if(GTK_IS_WINDOW(previous_toplevel)){g_signal_handlers_disconnect_by_func(previous_toplevel,FuncToGpointer(window_state_event_cb),widget);GdkWindow*win=gtk_widget_get_window(previous_toplevel);if(win){old_window_state=gdk_window_get_state(win);}}if(GTK_IS_WINDOW(toplevel)){g_signal_connect_swapped(toplevel,"window-state-event",G_CALLBACK(window_state_event_cb),widget);GdkWindow*win=gtk_widget_get_window(toplevel);if(win){event.new_window_state=gdk_window_get_state(win);}}event.changed_mask=static_cast<GdkWindowState>(old_window_state^event.new_window_state);if(event.changed_mask){event.type=GDK_WINDOW_STATE;event.window=nullptr;event.send_event=TRUE;window_state_event_cb(widget,&event);}}staticgbooleanwindow_state_event_cb(GtkWidget*widget,GdkEventWindowState*event){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window)returnFALSE;window->OnWindowStateEvent(widget,event);returnFALSE;}staticvoidtheme_changed_cb(GtkSettings*settings,GParamSpec*pspec,nsWindow*data){RefPtr<nsWindow>window=data;window->ThemeChanged();}staticvoidcheck_resize_cb(GtkContainer*container,gpointeruser_data){RefPtr<nsWindow>window=get_window_for_gtk_widget(GTK_WIDGET(container));if(!window){return;}window->OnCheckResize();}staticvoidcomposited_changed_cb(GtkWidget*widget,gpointeruser_data){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window){return;}window->OnCompositedChanged();}#if (MOZ_WIDGET_GTK == 3)staticvoidscale_changed_cb(GtkWidget*widget,GParamSpec*aPSpec,gpointeraPointer){RefPtr<nsWindow>window=get_window_for_gtk_widget(widget);if(!window){return;}window->OnDPIChanged();// configure_event is already fired before scale-factor signal,// but size-allocate isn't fired by changing scaleGtkAllocationallocation;gtk_widget_get_allocation(widget,&allocation);window->OnSizeAllocate(&allocation);}#endif#if GTK_CHECK_VERSION(3,4,0)staticgbooleantouch_event_cb(GtkWidget*aWidget,GdkEventTouch*aEvent){UpdateLastInputEventTime(aEvent);nsWindow*window=GetFirstNSWindowForGDKWindow(aEvent->window);if(!window){returnFALSE;}returnwindow->OnTouchEvent(aEvent);}#endif//////////////////////////////////////////////////////////////////////// These are all of our drag and drop operationsvoidnsWindow::InitDragEvent(WidgetDragEvent&aEvent){// set the keyboard modifiersguintmodifierState=KeymapWrapper::GetCurrentModifierState();KeymapWrapper::InitInputEvent(aEvent,modifierState);}staticgbooleandrag_motion_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,guintaTime,gpointeraData){RefPtr<nsWindow>window=get_window_for_gtk_widget(aWidget);if(!window)returnFALSE;// figure out which internal widget this drag motion actually happened onnscoordretx=0;nscoordrety=0;GdkWindow*innerWindow=get_inner_gdk_window(gtk_widget_get_window(aWidget),aX,aY,&retx,&rety);RefPtr<nsWindow>innerMostWindow=get_window_for_gdk_window(innerWindow);if(!innerMostWindow){innerMostWindow=window;}LOGDRAG(("nsWindow drag-motion signal for %p\n",(void*)innerMostWindow));LayoutDeviceIntPointpoint=window->GdkPointToDevicePixels({retx,rety});RefPtr<nsDragService>dragService=nsDragService::GetInstance();returndragService->ScheduleMotionEvent(innerMostWindow,aDragContext,point,aTime);}staticvoiddrag_leave_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,guintaTime,gpointeraData){RefPtr<nsWindow>window=get_window_for_gtk_widget(aWidget);if(!window)return;RefPtr<nsDragService>dragService=nsDragService::GetInstance();nsWindow*mostRecentDragWindow=dragService->GetMostRecentDestWindow();if(!mostRecentDragWindow){// This can happen when the target will not accept a drop. A GTK drag// source sends the leave message to the destination before the// drag-failed signal on the source widget, but the leave message goes// via the X server, and so doesn't get processed at least until the// event loop runs again.return;}GtkWidget*mozContainer=mostRecentDragWindow->GetMozContainerWidget();if(aWidget!=mozContainer){// When the drag moves between widgets, GTK can send leave signal for// the old widget after the motion or drop signal for the new widget.// We'll send the leave event when the motion or drop event is run.return;}LOGDRAG(("nsWindow drag-leave signal for %p\n",(void*)mostRecentDragWindow));dragService->ScheduleLeaveEvent();}staticgbooleandrag_drop_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,guintaTime,gpointeraData){RefPtr<nsWindow>window=get_window_for_gtk_widget(aWidget);if(!window)returnFALSE;// figure out which internal widget this drag motion actually happened onnscoordretx=0;nscoordrety=0;GdkWindow*innerWindow=get_inner_gdk_window(gtk_widget_get_window(aWidget),aX,aY,&retx,&rety);RefPtr<nsWindow>innerMostWindow=get_window_for_gdk_window(innerWindow);if(!innerMostWindow){innerMostWindow=window;}LOGDRAG(("nsWindow drag-drop signal for %p\n",(void*)innerMostWindow));LayoutDeviceIntPointpoint=window->GdkPointToDevicePixels({retx,rety});RefPtr<nsDragService>dragService=nsDragService::GetInstance();returndragService->ScheduleDropEvent(innerMostWindow,aDragContext,point,aTime);}staticvoiddrag_data_received_event_cb(GtkWidget*aWidget,GdkDragContext*aDragContext,gintaX,gintaY,GtkSelectionData*aSelectionData,guintaInfo,guintaTime,gpointeraData){RefPtr<nsWindow>window=get_window_for_gtk_widget(aWidget);if(!window)return;window->OnDragDataReceivedEvent(aWidget,aDragContext,aX,aY,aSelectionData,aInfo,aTime,aData);}staticnsresultinitialize_prefs(void){gRaiseWindows=Preferences::GetBool("mozilla.widget.raise-on-setfocus",true);returnNS_OK;}staticGdkWindow*get_inner_gdk_window(GdkWindow*aWindow,gintx,ginty,gint*retx,gint*rety){gintcx,cy,cw,ch;GList*children=gdk_window_peek_children(aWindow);for(GList*child=g_list_last(children);child;child=g_list_previous(child)){auto*childWindow=(GdkWindow*)child->data;if(get_window_for_gdk_window(childWindow)){#if (MOZ_WIDGET_GTK == 2)gdk_window_get_geometry(childWindow,&cx,&cy,&cw,&ch,nullptr);#elsegdk_window_get_geometry(childWindow,&cx,&cy,&cw,&ch);#endifif((cx<x)&&(x<(cx+cw))&&(cy<y)&&(y<(cy+ch))&&gdk_window_is_visible(childWindow)){returnget_inner_gdk_window(childWindow,x-cx,y-cy,retx,rety);}}}*retx=x;*rety=y;returnaWindow;}staticintis_parent_ungrab_enter(GdkEventCrossing*aEvent){return(GDK_CROSSING_UNGRAB==aEvent->mode)&&((GDK_NOTIFY_ANCESTOR==aEvent->detail)||(GDK_NOTIFY_VIRTUAL==aEvent->detail));}staticintis_parent_grab_leave(GdkEventCrossing*aEvent){return(GDK_CROSSING_GRAB==aEvent->mode)&&((GDK_NOTIFY_ANCESTOR==aEvent->detail)||(GDK_NOTIFY_VIRTUAL==aEvent->detail));}#ifdef ACCESSIBILITYvoidnsWindow::CreateRootAccessible(){if(mIsTopLevel&&!mRootAccessible){LOG(("nsWindow:: Create Toplevel Accessibility\n"));mRootAccessible=GetRootAccessible();}}voidnsWindow::DispatchEventToRootAccessible(uint32_taEventType){if(!a11y::ShouldA11yBeEnabled()){return;}nsAccessibilityService*accService=GetOrCreateAccService();if(!accService){return;}// Get the root document accessible and fire event to it.a11y::Accessible*acc=GetRootAccessible();if(acc){accService->FireAccessibleEvent(aEventType,acc);}}voidnsWindow::DispatchActivateEventAccessible(void){DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);}voidnsWindow::DispatchDeactivateEventAccessible(void){DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);}voidnsWindow::DispatchMaximizeEventAccessible(void){DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);}voidnsWindow::DispatchMinimizeEventAccessible(void){DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);}voidnsWindow::DispatchRestoreEventAccessible(void){DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);}#endif /* #ifdef ACCESSIBILITY */voidnsWindow::SetInputContext(constInputContext&aContext,constInputContextAction&aAction){if(!mIMContext){return;}mIMContext->SetInputContext(this,&aContext,&aAction);}InputContextnsWindow::GetInputContext(){InputContextcontext;if(!mIMContext){context.mIMEState.mEnabled=IMEState::DISABLED;context.mIMEState.mOpen=IMEState::OPEN_STATE_NOT_SUPPORTED;}else{context=mIMContext->GetInputContext();}returncontext;}TextEventDispatcherListener*nsWindow::GetNativeTextEventDispatcherListener(){if(NS_WARN_IF(!mIMContext)){returnnullptr;}returnmIMContext;}voidnsWindow::GetEditCommandsRemapped(NativeKeyBindingsTypeaType,constWidgetKeyboardEvent&aEvent,nsTArray<CommandInt>&aCommands,uint32_taGeckoKeyCode,uint32_taNativeKeyCode){WidgetKeyboardEventmodifiedEvent(aEvent);modifiedEvent.mKeyCode=aGeckoKeyCode;static_cast<GdkEventKey*>(modifiedEvent.mNativeKeyEvent)->keyval=aNativeKeyCode;NativeKeyBindings*keyBindings=NativeKeyBindings::GetInstance(aType);returnkeyBindings->GetEditCommands(modifiedEvent,aCommands);}voidnsWindow::GetEditCommands(NativeKeyBindingsTypeaType,constWidgetKeyboardEvent&aEvent,nsTArray<CommandInt>&aCommands){// Validate the arguments.nsIWidget::GetEditCommands(aType,aEvent,aCommands);if(aEvent.mKeyCode>=NS_VK_LEFT&&aEvent.mKeyCode<=NS_VK_DOWN){// Check if we're targeting content with vertical writing mode,// and if so remap the arrow keys.// XXX This may be expensive.WidgetQueryContentEventquery(true,eQuerySelectedText,this);nsEventStatusstatus;DispatchEvent(&query,status);if(query.mSucceeded&&query.mReply.mWritingMode.IsVertical()){uint32_tgeckoCode=0;uint32_tgdkCode=0;switch(aEvent.mKeyCode){caseNS_VK_LEFT:if(query.mReply.mWritingMode.IsVerticalLR()){geckoCode=NS_VK_UP;gdkCode=GDK_Up;}else{geckoCode=NS_VK_DOWN;gdkCode=GDK_Down;}break;caseNS_VK_RIGHT:if(query.mReply.mWritingMode.IsVerticalLR()){geckoCode=NS_VK_DOWN;gdkCode=GDK_Down;}else{geckoCode=NS_VK_UP;gdkCode=GDK_Up;}break;caseNS_VK_UP:geckoCode=NS_VK_LEFT;gdkCode=GDK_Left;break;caseNS_VK_DOWN:geckoCode=NS_VK_RIGHT;gdkCode=GDK_Right;break;}GetEditCommandsRemapped(aType,aEvent,aCommands,geckoCode,gdkCode);return;}}NativeKeyBindings*keyBindings=NativeKeyBindings::GetInstance(aType);keyBindings->GetEditCommands(aEvent,aCommands);}#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)/* static */already_AddRefed<DrawTarget>nsWindow::GetDrawTargetForGdkDrawable(GdkDrawable*aDrawable,constIntSize&aSize){GdkVisual*visual=gdk_drawable_get_visual(aDrawable);Screen*xScreen=gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable));Display*xDisplay=DisplayOfScreen(xScreen);DrawablexDrawable=gdk_x11_drawable_get_xid(aDrawable);RefPtr<gfxASurface>surface;if(visual){Visual*xVisual=gdk_x11_visual_get_xvisual(visual);surface=newgfxXlibSurface(xDisplay,xDrawable,xVisual,aSize);}else{// no visual? we must be using an xrender format. Find a format// for this depth.XRenderPictFormat*pf=nullptr;switch(gdk_drawable_get_depth(aDrawable)){case32:pf=XRenderFindStandardFormat(xDisplay,PictStandardARGB32);break;case24:pf=XRenderFindStandardFormat(xDisplay,PictStandardRGB24);break;default:NS_ERROR("Don't know how to handle the given depth!");break;}surface=newgfxXlibSurface(xScreen,xDrawable,pf,aSize);}RefPtr<DrawTarget>dt=gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface,aSize);if(!dt||!dt->IsValid()){returnnullptr;}returndt.forget();}#endifalready_AddRefed<DrawTarget>nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion&aInvalidRegion,BufferMode*aBufferMode){returnmSurfaceProvider.StartRemoteDrawingInRegion(aInvalidRegion,aBufferMode);}voidnsWindow::EndRemoteDrawingInRegion(DrawTarget*aDrawTarget,LayoutDeviceIntRegion&aInvalidRegion){mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget,aInvalidRegion);}// Code shared begin BeginMoveDrag and BeginResizeDragboolnsWindow::GetDragInfo(WidgetMouseEvent*aMouseEvent,GdkWindow**aWindow,gint*aButton,gint*aRootX,gint*aRootY){if(aMouseEvent->button!=WidgetMouseEvent::eLeftButton){// we can only begin a move drag with the left mouse buttonreturnfalse;}*aButton=1;// get the gdk window for this widgetGdkWindow*gdk_window=mGdkWindow;if(!gdk_window){returnfalse;}#ifdef DEBUG// GDK_IS_WINDOW(...) expands to a statement-expression, and// statement-expressions are not allowed in template-argument lists. So we// have to make the MOZ_ASSERT condition indirect.if(!GDK_IS_WINDOW(gdk_window)){MOZ_ASSERT(false,"must really be window");}#endif// find the top-level windowgdk_window=gdk_window_get_toplevel(gdk_window);MOZ_ASSERT(gdk_window,"gdk_window_get_toplevel should not return null");*aWindow=gdk_window;if(!aMouseEvent->mWidget){returnfalse;}// FIXME: It would be nice to have the widget position at the time// of the event, but it's relatively unlikely that the widget has// moved since the mousedown. (On the other hand, it's quite likely// that the mouse has moved, which is why we use the mouse position// from the event.)LayoutDeviceIntPointoffset=aMouseEvent->mWidget->WidgetToScreenOffset();*aRootX=aMouseEvent->mRefPoint.x+offset.x;*aRootY=aMouseEvent->mRefPoint.y+offset.y;returntrue;}nsresultnsWindow::BeginMoveDrag(WidgetMouseEvent*aEvent){MOZ_ASSERT(aEvent,"must have event");MOZ_ASSERT(aEvent->mClass==eMouseEventClass,"event must have correct struct type");GdkWindow*gdk_window;gintbutton,screenX,screenY;if(!GetDragInfo(aEvent,&gdk_window,&button,&screenX,&screenY)){returnNS_ERROR_FAILURE;}// tell the window manager to start the movescreenX=DevicePixelsToGdkCoordRoundDown(screenX);screenY=DevicePixelsToGdkCoordRoundDown(screenY);gdk_window_begin_move_drag(gdk_window,button,screenX,screenY,aEvent->mTime);returnNS_OK;}nsresultnsWindow::BeginResizeDrag(WidgetGUIEvent*aEvent,int32_taHorizontal,int32_taVertical){NS_ENSURE_ARG_POINTER(aEvent);if(aEvent->mClass!=eMouseEventClass){// you can only begin a resize drag with a mouse eventreturnNS_ERROR_INVALID_ARG;}GdkWindow*gdk_window;gintbutton,screenX,screenY;if(!GetDragInfo(aEvent->AsMouseEvent(),&gdk_window,&button,&screenX,&screenY)){returnNS_ERROR_FAILURE;}// work out what GdkWindowEdge we're talking aboutGdkWindowEdgewindow_edge;if(aVertical<0){if(aHorizontal<0){window_edge=GDK_WINDOW_EDGE_NORTH_WEST;}elseif(aHorizontal==0){window_edge=GDK_WINDOW_EDGE_NORTH;}else{window_edge=GDK_WINDOW_EDGE_NORTH_EAST;}}elseif(aVertical==0){if(aHorizontal<0){window_edge=GDK_WINDOW_EDGE_WEST;}elseif(aHorizontal==0){returnNS_ERROR_INVALID_ARG;}else{window_edge=GDK_WINDOW_EDGE_EAST;}}else{if(aHorizontal<0){window_edge=GDK_WINDOW_EDGE_SOUTH_WEST;}elseif(aHorizontal==0){window_edge=GDK_WINDOW_EDGE_SOUTH;}else{window_edge=GDK_WINDOW_EDGE_SOUTH_EAST;}}// tell the window manager to start the resizegdk_window_begin_resize_drag(gdk_window,window_edge,button,screenX,screenY,aEvent->mTime);returnNS_OK;}nsIWidget::LayerManager*nsWindow::GetLayerManager(PLayerTransactionChild*aShadowManager,LayersBackendaBackendHint,LayerManagerPersistenceaPersistence){if(mIsDestroyed){// Prevent external code from triggering the re-creation of the LayerManager/Compositor// during shutdown. Just return what we currently have, which is most likely null.returnmLayerManager;}if(!mLayerManager&&eTransparencyTransparent==GetTransparencyMode()){mLayerManager=CreateBasicLayerManager();}returnnsBaseWidget::GetLayerManager(aShadowManager,aBackendHint,aPersistence);}voidnsWindow::ClearCachedResources(){if(mLayerManager&&mLayerManager->GetBackendType()==mozilla::layers::LayersBackend::LAYERS_BASIC){mLayerManager->ClearCachedResources();}GList*children=gdk_window_peek_children(mGdkWindow);for(GList*list=children;list;list=list->next){nsWindow*window=get_window_for_gdk_window(GDK_WINDOW(list->data));if(window){window->ClearCachedResources();}}}gintnsWindow::GdkScaleFactor(){#if (MOZ_WIDGET_GTK >= 3)// Available as of GTK 3.10+staticautosGdkWindowGetScaleFactorPtr=(gint(*)(GdkWindow*))dlsym(RTLD_DEFAULT,"gdk_window_get_scale_factor");if(sGdkWindowGetScaleFactorPtr&&mGdkWindow)return(*sGdkWindowGetScaleFactorPtr)(mGdkWindow);#endifreturnScreenHelperGTK::GetGTKMonitorScaleFactor();}gintnsWindow::DevicePixelsToGdkCoordRoundUp(intpixels){gintscale=GdkScaleFactor();return(pixels+scale-1)/scale;}gintnsWindow::DevicePixelsToGdkCoordRoundDown(intpixels){gintscale=GdkScaleFactor();returnpixels/scale;}GdkPointnsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPointpoint){gintscale=GdkScaleFactor();return{point.x/scale,point.y/scale};}GdkRectanglensWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRectrect){gintscale=GdkScaleFactor();intx=rect.x/scale;inty=rect.y/scale;intright=(rect.x+rect.width+scale-1)/scale;intbottom=(rect.y+rect.height+scale-1)/scale;return{x,y,right-x,bottom-y};}GdkRectanglensWindow::DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSizepixelSize){gintscale=GdkScaleFactor();gintwidth=(pixelSize.width+scale-1)/scale;gintheight=(pixelSize.height+scale-1)/scale;return{0,0,width,height};}intnsWindow::GdkCoordToDevicePixels(gintcoord){returncoord*GdkScaleFactor();}LayoutDeviceIntPointnsWindow::GdkEventCoordsToDevicePixels(gdoublex,gdoubley){gintscale=GdkScaleFactor();returnLayoutDeviceIntPoint::Round(x*scale,y*scale);}LayoutDeviceIntPointnsWindow::GdkPointToDevicePixels(GdkPointpoint){gintscale=GdkScaleFactor();returnLayoutDeviceIntPoint(point.x*scale,point.y*scale);}LayoutDeviceIntRectnsWindow::GdkRectToDevicePixels(GdkRectanglerect){gintscale=GdkScaleFactor();returnLayoutDeviceIntRect(rect.x*scale,rect.y*scale,rect.width*scale,rect.height*scale);}nsresultnsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPointaPoint,uint32_taNativeMessage,uint32_taModifierFlags,nsIObserver*aObserver){AutoObserverNotifiernotifier(aObserver,"mouseevent");if(!mGdkWindow){returnNS_OK;}GdkDisplay*display=gdk_window_get_display(mGdkWindow);// When a button-press/release event is requested, create it here and put it in the// event queue. This will not emit a motion event - this needs to be done// explicitly *before* requesting a button-press/release. You will also need to wait// for the motion event to be dispatched before requesting a button-press/release// event to maintain the desired event order.if(aNativeMessage==GDK_BUTTON_PRESS||aNativeMessage==GDK_BUTTON_RELEASE){GdkEventevent;memset(&event,0,sizeof(GdkEvent));event.type=(GdkEventType)aNativeMessage;event.button.button=1;event.button.window=mGdkWindow;event.button.time=GDK_CURRENT_TIME;#if (MOZ_WIDGET_GTK == 3)// Get device for event sourceGdkDeviceManager*device_manager=gdk_display_get_device_manager(display);event.button.device=gdk_device_manager_get_client_pointer(device_manager);#endifevent.button.x_root=DevicePixelsToGdkCoordRoundDown(aPoint.x);event.button.y_root=DevicePixelsToGdkCoordRoundDown(aPoint.y);LayoutDeviceIntPointpointInWindow=aPoint-WidgetToScreenOffset();event.button.x=DevicePixelsToGdkCoordRoundDown(pointInWindow.x);event.button.y=DevicePixelsToGdkCoordRoundDown(pointInWindow.y);gdk_event_put(&event);}else{// We don't support specific events other than button-press/release. In all// other cases we'll synthesize a motion event that will be emitted by// gdk_display_warp_pointer().GdkScreen*screen=gdk_window_get_screen(mGdkWindow);GdkPointpoint=DevicePixelsToGdkPointRoundDown(aPoint);gdk_display_warp_pointer(display,screen,point.x,point.y);}returnNS_OK;}nsresultnsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPointaPoint,uint32_taNativeMessage,doubleaDeltaX,doubleaDeltaY,doubleaDeltaZ,uint32_taModifierFlags,uint32_taAdditionalFlags,nsIObserver*aObserver){AutoObserverNotifiernotifier(aObserver,"mousescrollevent");if(!mGdkWindow){returnNS_OK;}GdkEventevent;memset(&event,0,sizeof(GdkEvent));event.type=GDK_SCROLL;event.scroll.window=mGdkWindow;event.scroll.time=GDK_CURRENT_TIME;#if (MOZ_WIDGET_GTK == 3)// Get device for event sourceGdkDisplay*display=gdk_window_get_display(mGdkWindow);GdkDeviceManager*device_manager=gdk_display_get_device_manager(display);event.scroll.device=gdk_device_manager_get_client_pointer(device_manager);#endifevent.scroll.x_root=DevicePixelsToGdkCoordRoundDown(aPoint.x);event.scroll.y_root=DevicePixelsToGdkCoordRoundDown(aPoint.y);LayoutDeviceIntPointpointInWindow=aPoint-WidgetToScreenOffset();event.scroll.x=DevicePixelsToGdkCoordRoundDown(pointInWindow.x);event.scroll.y=DevicePixelsToGdkCoordRoundDown(pointInWindow.y);// The delta values are backwards on Linux compared to Windows and Cocoa,// hence the negation.#if GTK_CHECK_VERSION(3,4,0)// TODO: is this correct? I don't have GTK 3.4+ so I can't checkevent.scroll.direction=GDK_SCROLL_SMOOTH;event.scroll.delta_x=-aDeltaX;event.scroll.delta_y=-aDeltaY;#elseif(aDeltaX<0){event.scroll.direction=GDK_SCROLL_RIGHT;}elseif(aDeltaX>0){event.scroll.direction=GDK_SCROLL_LEFT;}elseif(aDeltaY<0){event.scroll.direction=GDK_SCROLL_DOWN;}elseif(aDeltaY>0){event.scroll.direction=GDK_SCROLL_UP;}else{returnNS_OK;}#endifgdk_event_put(&event);returnNS_OK;}#if GTK_CHECK_VERSION(3,4,0)nsresultnsWindow::SynthesizeNativeTouchPoint(uint32_taPointerId,TouchPointerStateaPointerState,LayoutDeviceIntPointaPoint,doubleaPointerPressure,uint32_taPointerOrientation,nsIObserver*aObserver){AutoObserverNotifiernotifier(aObserver,"touchpoint");if(!mGdkWindow){returnNS_OK;}GdkEventevent;memset(&event,0,sizeof(GdkEvent));staticstd::map<uint32_t,GdkEventSequence*>sKnownPointers;autoresult=sKnownPointers.find(aPointerId);switch(aPointerState){caseTOUCH_CONTACT:if(result==sKnownPointers.end()){// GdkEventSequence isn't a thing we can instantiate, and never gets// dereferenced in the gtk code. It's an opaque pointer, the only// requirement is that it be distinct from other instances of// GdkEventSequence*.event.touch.sequence=(GdkEventSequence*)((uintptr_t)aPointerId);sKnownPointers[aPointerId]=event.touch.sequence;event.type=GDK_TOUCH_BEGIN;}else{event.touch.sequence=result->second;event.type=GDK_TOUCH_UPDATE;}break;caseTOUCH_REMOVE:event.type=GDK_TOUCH_END;if(result==sKnownPointers.end()){NS_WARNING("Tried to synthesize touch-end for unknown pointer!");returnNS_ERROR_UNEXPECTED;}event.touch.sequence=result->second;sKnownPointers.erase(result);break;caseTOUCH_CANCEL:event.type=GDK_TOUCH_CANCEL;if(result==sKnownPointers.end()){NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!");returnNS_ERROR_UNEXPECTED;}event.touch.sequence=result->second;sKnownPointers.erase(result);break;caseTOUCH_HOVER:default:returnNS_ERROR_NOT_IMPLEMENTED;}event.touch.window=mGdkWindow;event.touch.time=GDK_CURRENT_TIME;GdkDisplay*display=gdk_window_get_display(mGdkWindow);GdkDeviceManager*device_manager=gdk_display_get_device_manager(display);event.touch.device=gdk_device_manager_get_client_pointer(device_manager);event.touch.x_root=DevicePixelsToGdkCoordRoundDown(aPoint.x);event.touch.y_root=DevicePixelsToGdkCoordRoundDown(aPoint.y);LayoutDeviceIntPointpointInWindow=aPoint-WidgetToScreenOffset();event.touch.x=DevicePixelsToGdkCoordRoundDown(pointInWindow.x);event.touch.y=DevicePixelsToGdkCoordRoundDown(pointInWindow.y);gdk_event_put(&event);returnNS_OK;}#endifint32_tnsWindow::RoundsWidgetCoordinatesTo(){returnGdkScaleFactor();}voidnsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData*aInitData){#ifdef MOZ_X11*aInitData=mozilla::widget::CompositorWidgetInitData(mXWindow,nsCString(XDisplayString(mXDisplay)),GetClientSize());#endif}boolnsWindow::IsComposited()const{if(!mGdkWindow){NS_WARNING("nsWindow::HasARGBVisual called before realization!");returnfalse;}GdkScreen*gdkScreen=gdk_screen_get_default();returngdk_screen_is_composited(gdkScreen)&&(gdk_window_get_visual(mGdkWindow)==gdk_screen_get_rgba_visual(gdkScreen));}